Merge branch 'release/3.5.0'

This commit is contained in:
Nathan Chapman 2023-07-31 20:31:45 -06:00
commit 6a95facf9e
45 changed files with 637 additions and 612 deletions

View File

@ -1,15 +1,50 @@
from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import get_user_model from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.admin import UserAdmin
from .models import User from .models import User
from .forms import AccountCreateForm, AccountUpdateForm from .forms import UserCreateForm, UserUpdateForm
class UserAdmin(UserAdmin):
add_form = AccountCreateForm class UserAdmin(BaseUserAdmin):
form = AccountUpdateForm # The forms to add and change user instances
model = User form = UserUpdateForm
list_display = ['email', 'username',] add_form = UserCreateForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
fieldsets = (
(None, {"fields": ("email", "password")}),
("Personal info", {"fields": ("first_name", "last_name")}),
(
"Permissions",
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
("Important dates", {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)
list_display = ("email", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active", "groups")
search_fields = ("email", "first_name", "last_name")
ordering = ["email"]
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)

View File

@ -1,29 +1,74 @@
from django import forms from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm from django.contrib.auth.forms import PasswordChangeForm, UserCreationForm
from allauth.account.forms import SignupForm from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from captcha.fields import CaptchaField from captcha.fields import CaptchaField
from .models import User from .models import User
class AccountCreateForm(UserCreationForm): class UserCreateForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
required_css_class = "field-required"
password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
password2 = forms.CharField(
label="Password confirmation", widget=forms.PasswordInput
)
class Meta: class Meta:
model = User model = User
fields = ('username', 'email') fields = ["email", "first_name", "last_name"]
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class AccountUpdateForm(UserChangeForm): class UserUpdateForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
disabled password hash display field.
"""
required_css_class = "field-required"
password = ReadOnlyPasswordHashField()
class Meta: class Meta:
model = User model = User
fields = ( fields = [
'first_name', "email",
'last_name', "password",
'email', "first_name",
"last_name",
'shipping_street_address_1', 'shipping_street_address_1',
'shipping_street_address_2', 'shipping_street_address_2',
'shipping_city', 'shipping_city',
'shipping_state', 'shipping_state',
'shipping_postal_code', 'shipping_postal_code',
) "is_active",
"is_superuser"
]
labels = {
'shipping_street_address_1': 'Street line 1',
'shipping_street_address_2': 'Street line 2',
'shipping_city': 'City',
'shipping_state': 'State',
'shipping_postal_code': 'ZIP code',
}
class CustomerUpdateForm(forms.ModelForm): class CustomerUpdateForm(forms.ModelForm):
@ -55,14 +100,15 @@ class CustomerShippingAddressUpdateForm(forms.ModelForm):
} }
class UserSignupForm(SignupForm): class AccountCreateForm(UserCreationForm):
first_name = forms.CharField( required_css_class = "field-required"
required=True,
widget=forms.TextInput(attrs={'placeholder': 'First name'})
)
last_name = forms.CharField(
required=True,
widget=forms.TextInput(attrs={'placeholder': 'Last name'})
)
captcha = CaptchaField() captcha = CaptchaField()
class Meta:
model = User
fields = [
"email",
"first_name",
"last_name",
]

View File

@ -0,0 +1,38 @@
# Generated by Django 4.1.6 on 2023-07-31 23:54
import accounts.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0005_remove_user_addresses_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='user',
options={},
),
migrations.AlterModelManagers(
name='user',
managers=[
('objects', accounts.models.UserManager()),
],
),
migrations.RemoveField(
model_name='user',
name='username',
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(max_length=255, unique=True, verbose_name='email address'),
),
migrations.AlterField(
model_name='user',
name='is_active',
field=models.BooleanField(default=True, help_text='Designates whether this user should be treated as active.\nUnselect this instead of deleting accounts.', verbose_name='active'),
),
]

View File

@ -1,12 +1,89 @@
import stripe import stripe
from django.apps import apps
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.contrib.auth.models import AbstractUser from django.core.mail import send_mail
from django.utils import timezone as tz
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser, PermissionsMixin
)
from localflavor.us.us_states import USPS_CHOICES from localflavor.us.us_states import USPS_CHOICES
class User(AbstractUser): class UserManager(BaseUserManager):
stripe_id = models.CharField(max_length=255, blank=True) use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Create and save a user with the given email and password.
"""
if not email:
raise ValueError("The given email must be set")
email = self.normalize_email(email)
# Lookup the real model class from the global app registry so this
# manager method can be used in migrations. This is fine because
# managers are by definition working on the real model.
GlobalUserModel = apps.get_model(
self.model._meta.app_label, self.model._meta.object_name
)
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, **extra_fields)
def with_perm(
self, perm, is_active=True, include_superusers=True, backend=None, obj=None
):
if backend is None:
backends = auth._get_backends(return_tuples=True)
if len(backends) == 1:
backend, _ = backends[0]
else:
raise ValueError(
"You have multiple authentication backends configured and "
"therefore must provide the `backend` argument."
)
elif not isinstance(backend, str):
raise TypeError(
"backend must be a dotted import path string (got %r)." % backend
)
else:
backend = auth.load_backend(backend)
if hasattr(backend, "with_perm"):
return backend.with_perm(
perm,
is_active=is_active,
include_superusers=include_superusers,
obj=obj,
)
return self.none()
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name="email address",
max_length=255,
unique=True,
)
first_name = models.CharField("first name", max_length=150, blank=True)
last_name = models.CharField("last name", max_length=150, blank=True)
# Shipping address # Shipping address
shipping_street_address_1 = models.CharField(max_length=256, blank=True) shipping_street_address_1 = models.CharField(max_length=256, blank=True)
@ -19,6 +96,25 @@ class User(AbstractUser):
) )
shipping_postal_code = models.CharField(max_length=20, blank=True) shipping_postal_code = models.CharField(max_length=20, blank=True)
is_staff = models.BooleanField(
"staff status",
default=False,
help_text="Designates whether the user can log into this admin site.",
)
is_active = models.BooleanField(
"active",
default=True,
help_text="Designates whether this user should be treated as active.\n"
+ "Unselect this instead of deleting accounts."
)
date_joined = models.DateTimeField("date joined", default=tz.now)
stripe_id = models.CharField(max_length=255, blank=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
objects = UserManager()
@property @property
def has_shipping_address(self): def has_shipping_address(self):
if (self.shipping_street_address_1 != '' if (self.shipping_street_address_1 != ''
@ -29,12 +125,35 @@ class User(AbstractUser):
return True return True
return False return False
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""
Return the first_name plus the last_name, with a space in between.
"""
full_name = "%s %s" % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"""Return the short name for the user."""
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
def get_or_create_stripe_id(self): def get_or_create_stripe_id(self):
if not self.stripe_id: if not self.stripe_id:
response = stripe.Customer.create( response = stripe.Customer.create(
name=self.first_name + ' ' + self.last_name, name=self.get_full_name(),
email=self.email email=self.email
) )
self.stripe_id = response['id'] self.stripe_id = response["id"]
self.save() self.save()
return self.stripe_id return self.stripe_id
def get_absolute_url(self):
return reverse("storefront:customer-detail", kwargs={"pk": self.pk})

View File

@ -1,14 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<section>
<h1>Sign up</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Create account">
</form>
</section>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends 'base.html' %}
{% block content %}
<article>
<section>
<h1>Sign up</h1>
<form method="post">
{% csrf_token %}
<table class="form-table">
{{ form.as_table }}
<tr>
<td></td>
<td>
<button type="submit">Create account</button>
</td>
</tr>
</table>
</form>
</section>
</article>
{% endblock %}

52
accounts/templates/accounts/account_detail.html Executable file → Normal file
View File

@ -1,13 +1,49 @@
{% extends 'base.html' %} {% extends "base.html" %}
{% block head_title %}{{ object.get_full_name }} | {% endblock head_title %}
{% block content %} {% block content %}
<article class="panel"> <header class="page-header">
<h3>{{ object.get_full_name }}</h3>
{% if request.user.is_superuser or request.user == object%}
<a href="{% url 'account-update' object.pk %}">Update account</a>
{% endif %}
</header>
<section> <section>
<h1>{{ user.first_name }} {{ user.last_name }}</h1> <table>
<p>{{ user.email }}</p> <tbody>
<p> <tr>
<a href="{% url 'account-update' user.id %}">Update email/change password</a> <th>Email:</th>
</p> <td>{{ object.email }}</td>
</tr>
</tbody>
</table>
</section>
<br><br>
<section>
<h4>Students</h4>
<table class="index">
<thead>
<tr>
<th>Student</th>
<th>Membership</th>
</tr>
</thead>
<tbody>
{% for student in object.student_set.all %}
<tr>
<td><a href="{{ student.get_absolute_url }}">{{ student.full_name }}</a></td>
<td {% if not student.membership %}class="text-danger"{% endif %}>{{ student.membership }}</td>
</tr>
{% empty %}
<tr>
<td colspan="2">Could not find any students</td>
</tr>
{% endfor %}
</tbody>
</table>
</section> </section>
</article>
{% endblock %} {% endblock %}

19
accounts/templates/accounts/account_form.html Executable file → Normal file
View File

@ -1,17 +1,22 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<article> <header class="page-header">
<h3>Update Profile</h3>
</header>
<section> <section>
<h1>Update Profile</h1>
<p><a href="{% url 'password_change' %}">Change password</a></p> <p><a href="{% url 'password_change' %}">Change password</a></p>
<form method="post" action="{% url 'account-update' user.id %}"> <form id="account-form" method="post" action="{% url 'account-update' user.pk %}">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Save changes" class="btn"> or
<a href="{% url 'account-detail' user.id %}">Cancel</a>
</form> </form>
</section> </section>
</article>
<footer>
<p>
<button form="account-form" type="submit" class="action-button">Save changes</button> or
<a href="{% url 'account-detail' user.pk %}">Cancel</a>
</p>
</footer>
{% endblock %} {% endblock %}

22
accounts/templates/accounts/account_list.html Executable file → Normal file
View File

@ -1,21 +1,31 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<header class="page-header">
<h3>Users</h3>
<menu>
<a href="{% url 'account-create' %}">
<button class="btn btn-add">Create new account</button>
</a>
</menu>
</header>
<section> <section>
<h1>Users</h1> <table class="index">
<table>
<thead> <thead>
<th>Username</th>
<th>Name</th> <th>Name</th>
<th>Email</th>
</thead> </thead>
<tbody> <tbody>
{% for user in user_list %} {% for user in user_list %}
<tr> <tr>
<td>{{ user.username }}</td> <td><a href="{% url 'account-detail' user.id %}">{{ user.get_full_name }}</a></td>
<td><a href="{% url 'account-detail' user.id %}">{{user.first_name}} {{user.last_name}}</a></td> <td><a href="mailto:{{ user.email }}">{{ user.email }} &nearr;</a></td>
</tr> </tr>
{% empty %} {% empty %}
<tr><td>No users yet.</td></tr> <tr>
<td colspan="3">No users yet.</td>
</tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% block head_title %}Accounts |{% endblock %}
{% block tabs %}
{% include 'core/_partials/tabs.html' with active_tab='accounts' %}
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block content %}
<article>
<section>
<p>You have been logged out. <a href="{% url 'login' %}">Log in</a></p>
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,58 @@
{% extends 'base.html' %}
{% block content %}
<article>
<h1>Log in</h1>
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed, please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table class="form-table">
<tr>
<td>
{{ form.username.label_tag }}
</td>
<td>
{{ form.username }}
</td>
</tr>
<tr>
<td>
{{ form.password.label_tag }}
</td>
<td>
{{ form.password }}
</td>
</tr>
<tr>
<td></td>
<td>
<small>
Forgot your password?
<a class="password__reset hover" href="{% url 'password_reset' %}">Reset your password here</a>.
</small>
</td>
</tr>
<tr>
<td></td>
<td>
<button type="submit">Log in</button>
</td>
</tr>
</table>
<input type="hidden" name="next" value="{{ next }}">
</form>
</article>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<article>
<section>
<p>
Password has been changed.
<a href="{% url 'login' %}">Log in</a>
</p>
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block content %}
<article>
<p>
<a href="{% url 'storefront:customer-detail' user.pk %}">&larr; Back</a>
</p>
<form id="password-form" method="post">
{% csrf_token %}
<h3>Change password</h3>
<table class="form-table">
{{ form.as_table }}
<tr>
<td></td>
<td>
<button type="submit" form="password-form" class="action-button">Change my password</button>
</td>
</tr>
</table>
</form>
</article>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<article class="panel"> <article>
<p>Password was reset successfully.</p> <p>Password was reset successfully.</p>
</article> </article>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,19 @@
{% extends 'base.html' %}
{% block content %}
<article>
{% if validlink %}
<h1>Reset password</h1>
<p>Enter a <em>new</em> password below.</p>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<p>
<button type="submit" class="action-button">Reset your password</button>
</p>
</form>
{% else %}
<h1 class="text-danger">Password reset failed</h1>
{% endif %}
</article>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<article>
<p>An email with password reset instructions has been sent.</p>
</article>
{% endblock %}

View File

@ -0,0 +1,10 @@
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:
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
Your username, in case youve forgotten: {{ user.get_username }}
Sincerely,
Cache Valley Martial Arts & Fitness

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block content %}
<article>
<h1>Reset your password</h1>
<p>Enter your email address below and we'll send you instructions on how to reset your password.</p>
<form method="post" action="{% url 'password_reset' %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<button type="submit">Send me instructions</button>
</p>
</form>
</article>
{% endblock %}

View File

@ -1,11 +1,46 @@
from django.urls import path, include from django.urls import path, include
from django.contrib.auth import views as auth_views
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('', views.AccountListView.as_view(), name='account-list'), path("new/", views.AccountCreateView.as_view(), name="signup"),
path('<int:pk>/', include([ path(
path('', views.AccountDetailView.as_view(), name='account-detail'), "login/",
path('update/', views.AccountUpdateView.as_view(), name='account-update'), auth_views.LoginView.as_view(template_name="accounts/login.html"),
path('delete/', views.AccountDeleteView.as_view(), name='account-delete'), name="login"
])), ),
path("logout/", auth_views.LogoutView.as_view(template_name="accounts/logged_out.html"),
name="logout"
),
path(
"password-change/",
auth_views.PasswordChangeView.as_view(template_name="accounts/password_change_form.html"),
name="password_change"
),
path(
"password-change/done/",
auth_views.PasswordChangeDoneView.as_view(template_name="accounts/password_change_done.html"),
name="password_change_done"
),
path(
"password-reset/",
auth_views.PasswordResetView.as_view(template_name="accounts/password_reset_form.html"),
name="password_reset"
),
path(
"password-reset/done/",
auth_views.PasswordResetDoneView.as_view(template_name="accounts/password_reset_done.html"),
name="password_reset_done"
),
path(
"reset/<uidb64>/<token>/",
auth_views.PasswordResetConfirmView.as_view(template_name="accounts/password_reset_confirm.html"),
name="password_reset_confirm"
),
path(
"reset/done/",
auth_views.PasswordResetCompleteView.as_view(template_name="accounts/password_reset_complete.html"),
name="password_reset_complete"
),
] ]

View File

@ -1,38 +1,53 @@
from django.shortcuts import render, reverse, redirect from django.shortcuts import render, reverse, redirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.http import HttpResponseRedirect
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.list import ListView from django.views.generic.list import ListView
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.forms import PasswordChangeForm from django.contrib.auth.forms import PasswordChangeForm
from .models import User from .models import User
from .forms import AccountUpdateForm from .forms import UserCreateForm
class AccountListView(LoginRequiredMixin, ListView): class AccountCreateView(CreateView, SuccessMessageMixin):
model = User model = User
template_name = 'accounts/account_list.html' template_name = "accounts/account_create_form.html"
form_class = UserCreateForm
success_message = "Account created. You are now logged in."
class AccountDetailView(LoginRequiredMixin, DetailView): def get_user(self, uidb64):
model = User try:
template_name = 'accounts/account_detail.html' # urlsafe_base64_decode() decodes to bytestring
uid = urlsafe_base64_decode(uidb64).decode()
user = UserModel._default_manager.get(pk=uid)
except (
TypeError,
ValueError,
OverflowError,
UserModel.DoesNotExist,
ValidationError,
):
user = None
return user
def get_context_data(self, **kwargs): def form_valid(self, form):
context = super().get_context_data(**kwargs) self.object = form.save()
return context user = authenticate(
self.request,
class AccountUpdateView(LoginRequiredMixin, UpdateView): username=form.cleaned_data['email'],
model = User password=form.cleaned_data['password1']
form_class = AccountUpdateForm )
template_name = 'accounts/account_form.html' if user is not None:
login(self.request, user)
return HttpResponseRedirect(self.get_success_url())
return self.form_invalid(form)
def get_success_url(self): def get_success_url(self):
pk = self.kwargs["pk"] return reverse("storefront:customer-detail", kwargs={"pk": self.object.pk})
return reverse('account-detail', kwargs={'pk': pk})
class AccountDeleteView(LoginRequiredMixin, DeleteView):
model = User
success_url = reverse_lazy('account-list')

102
poetry.lock generated
View File

@ -447,17 +447,6 @@ sdist = ["setuptools-rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"] ssh = ["bcrypt (>=3.1.5)"]
test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
[[package]]
name = "defusedxml"
version = "0.7.1"
description = "XML bomb protection for Python stdlib modules"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
{file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
]
[[package]] [[package]]
name = "django" name = "django"
version = "4.1.6" version = "4.1.6"
@ -478,23 +467,6 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""}
argon2 = ["argon2-cffi (>=19.1.0)"] argon2 = ["argon2-cffi (>=19.1.0)"]
bcrypt = ["bcrypt"] bcrypt = ["bcrypt"]
[[package]]
name = "django-allauth"
version = "0.52.0"
description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication."
optional = false
python-versions = ">=3.5"
files = [
{file = "django-allauth-0.52.0.tar.gz", hash = "sha256:e380661ceafe55734c40102819ae720403027036f28e9f9827f0faeddc24ed5f"},
]
[package.dependencies]
Django = ">=2.0"
pyjwt = {version = ">=1.7", extras = ["crypto"]}
python3-openid = ">=3.0.8"
requests = "*"
requests-oauthlib = ">=0.3.0"
[[package]] [[package]]
name = "django-analytical" name = "django-analytical"
version = "3.1.0" version = "3.1.0"
@ -917,22 +889,6 @@ files = [
develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"]
tests = ["pytest (>=4.6)"] tests = ["pytest (>=4.6)"]
[[package]]
name = "oauthlib"
version = "3.2.2"
description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
optional = false
python-versions = ">=3.6"
files = [
{file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"},
{file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"},
]
[package.extras]
rsa = ["cryptography (>=3.0.0)"]
signals = ["blinker (>=1.4.0)"]
signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
[[package]] [[package]]
name = "outcome" name = "outcome"
version = "1.2.0" version = "1.2.0"
@ -1213,26 +1169,6 @@ files = [
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
] ]
[[package]]
name = "pyjwt"
version = "2.6.0"
description = "JSON Web Token implementation in Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"},
{file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"},
]
[package.dependencies]
cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""}
[package.extras]
crypto = ["cryptography (>=3.4.0)"]
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]] [[package]]
name = "pyopenssl" name = "pyopenssl"
version = "23.0.0" version = "23.0.0"
@ -1293,24 +1229,6 @@ soap = ["zeep"]
soap-alt = ["suds"] soap-alt = ["suds"]
soap-fallback = ["PySimpleSOAP"] soap-fallback = ["PySimpleSOAP"]
[[package]]
name = "python3-openid"
version = "3.2.0"
description = "OpenID support for modern servers and consumers."
optional = false
python-versions = "*"
files = [
{file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"},
{file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"},
]
[package.dependencies]
defusedxml = "*"
[package.extras]
mysql = ["mysql-connector-python"]
postgresql = ["psycopg2"]
[[package]] [[package]]
name = "pytz" name = "pytz"
version = "2022.7.1" version = "2022.7.1"
@ -1393,24 +1311,6 @@ urllib3 = ">=1.21.1,<1.27"
socks = ["PySocks (>=1.5.6,!=1.5.7)"] socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "requests-oauthlib"
version = "1.3.1"
description = "OAuthlib authentication support for Requests."
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"},
{file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"},
]
[package.dependencies]
oauthlib = ">=3.0.0"
requests = ">=2.0.0"
[package.extras]
rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
[[package]] [[package]]
name = "rjsmin" name = "rjsmin"
version = "1.2.1" version = "1.2.1"
@ -1728,4 +1628,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = "^3.11"
content-hash = "fc52ce089cd76070e64e3f927f18a8f3a96e0b27e5377a9453d400520250f3f2" content-hash = "172f51231b971fe70bfd8a2b54b8c1778c95430dbca232d1e6f69ed305141650"

View File

@ -75,9 +75,6 @@ INSTALLED_APPS = [
'localflavor', 'localflavor',
'anymail', 'anymail',
'compressor', 'compressor',
'allauth',
'allauth.account',
'allauth.socialaccount',
'analytical', 'analytical',
'captcha', 'captcha',
@ -196,27 +193,10 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
]
AUTH_USER_MODEL = 'accounts.User' AUTH_USER_MODEL = 'accounts.User'
# All-auth
# https://django-allauth.readthedocs.io/en/latest/installation.html
ACCOUNT_FORMS = {'signup': 'accounts.forms.UserSignupForm'}
ACCOUNT_ADAPTER = 'accounts.adapter.AccountAdapter'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = True
ACCOUNT_SESSION_REMEMBER = True
ACCOUNT_UNIQUE_EMAIL = True
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/ # https://docs.djangoproject.com/en/4.1/topics/i18n/

View File

@ -6,8 +6,7 @@ from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
path('', include(('storefront.urls', 'storefront'), namespace='storefront')), path('', include(('storefront.urls', 'storefront'), namespace='storefront')),
path('dashboard/', include(('dashboard.urls', 'dashboard'), namespace='dashboard')), path('dashboard/', include(('dashboard.urls', 'dashboard'), namespace='dashboard')),
path('accounts/', include('allauth.urls')), path("accounts/", include("accounts.urls")),
path('accounts/', include(('accounts.urls', 'accounts'), namespace='accounts')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('captcha/', include('captcha.urls')), path('captcha/', include('captcha.urls')),
] ]

View File

@ -9,7 +9,6 @@ readme = "README.md"
python = "^3.11" python = "^3.11"
django = "^4.1.5" django = "^4.1.5"
celery = {extras = ["redis"], version = "^5.2.7"} celery = {extras = ["redis"], version = "^5.2.7"}
django-allauth = "^0.52.0"
django-anymail = {extras = ["mailgun"], version = "^9.0"} django-anymail = {extras = ["mailgun"], version = "^9.0"}
django-compressor = "^4.1" django-compressor = "^4.1"
django-filter = "^22.1" django-filter = "^22.1"

View File

@ -22,26 +22,12 @@ closeBtn.addEventListener("click", event => {
showBtn.addEventListener("click", event => { showBtn.addEventListener("click", event => {
modal.style.display = "flex"; modal.style.display = "flex";
setCookie('newsletter-modal', 'true', oneDay)
}) })
const scrollFunction = () => {
let modalDismissed = getCookie('newsletter-modal')
if (modalDismissed != 'true') {
if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) {
modal.style.display = "flex";
}
}
}
window.onscroll = () => {
scrollFunction();
};
// When the user clicks anywhere outside of the modal, close it // When the user clicks anywhere outside of the modal, close it
window.addEventListener("click", event => { window.addEventListener("click", event => {
if (event.target == modal) { if (event.target == modal) {
modal.style.display = "none"; modal.style.display = "none";
} }
setCookie('newsletter-modal', 'true', oneDay)
}); });

View File

@ -54,7 +54,6 @@ h1, h2, h3, h4, h5 {
h1 { h1 {
margin-top: 0; margin-top: 0;
font-family: 'Vollkorn', serif;
font-size: 2.488rem; font-size: 2.488rem;
} }
@ -129,16 +128,17 @@ table a {
.form-table { .form-table {
width: unset; width: unset;
border: none; border: none;
border-collapse: separate;
border-spacing: 0 1rem;
} }
.form-table th, .form-table th,
.form-table td { .form-table td {
border: none; border: none;
} }
.form-table th { .form-table th,
padding: 0 1rem 1rem 0;
}
.form-table td { .form-table td {
padding: 0 0 1rem 0; padding: 0 1rem 1rem 0;
vertical-align: top;
} }
@ -473,7 +473,6 @@ section:not(:last-child) {
text-align: center; text-align: center;
padding: 2rem 1rem; padding: 2rem 1rem;
text-shadow: 1px 1px 2px black; text-shadow: 1px 1px 2px black;
font-family: 'Vollkorn', serif;
} }
.carousel { .carousel {
@ -499,7 +498,6 @@ section:not(:last-child) {
padding: 2rem 1rem; padding: 2rem 1rem;
box-sizing: border-box; box-sizing: border-box;
text-shadow: 1px 1px 2px black; text-shadow: 1px 1px 2px black;
font-family: 'Vollkorn', serif;
color: white; color: white;
display: flex; display: flex;
@ -750,7 +748,6 @@ article + article {
.product__item h3, .product__item h3,
.product__info h1 { .product__info h1 {
/*text-decoration: underline;*/ /*text-decoration: underline;*/
font-family: "Vollkorn", serif;
font-weight: 900; font-weight: 900;
} }

View File

@ -8,7 +8,7 @@
<section> <section>
<h2>My Account</h2> <h2>My Account</h2>
<p> <p>
<a href="{% url 'account_change_password' %}">Change password</a> <a href="{% url 'password_change' %}">Change password</a>
</p> </p>
<div class="customer__detail-section"> <div class="customer__detail-section">
<div> <div>

View File

@ -1,76 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% block head_title %}{% trans "E-mail Addresses" %}{% endblock %}
{% block content %}
<article>
<p><a href="{% url 'storefront:customer-detail' user.pk %}">&larr; Back</a></p>
<section>
<h1>{% trans "E-mail Addresses" %}</h1>
</section>
<section class="user__emails">
{% if user.emailaddress_set.all %}
<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p>
<form action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %}
<fieldset>
{% for emailaddress in user.emailaddress_set.all %}
<div>
<input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked"{%endif %} value="{{emailaddress.email}}"/>
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
{{ emailaddress.email }}
{% if emailaddress.verified %}
<span class="verified">{% trans "Verified" %}</span>
{% else %}
<span class="unverified">{% trans "Unverified" %}</span>
{% endif %}
{% if emailaddress.primary %}<span class="primary">{% trans "Primary" %}</span>{% endif %}
</label>
</div>
{% endfor %}
<div>
<button type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
<button type="submit" name="action_send" >{% trans 'Re-send Verification' %}</button>
<button type="submit" name="action_remove" >{% trans 'Remove' %}</button>
</div>
</fieldset>
</form>
{% else %}
<p><strong>{% trans 'Warning:'%}</strong> {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}</p>
{% endif %}
</section>
<section>
{% if can_add_email %}
<h2>{% trans "Add E-mail Address" %}</h2>
<form method="post" action="{% url 'account_email' %}" class="add_email">
{% csrf_token %}
{{ form.as_p }}
<button name="action_add" type="submit">{% trans "Add E-mail" %}</button>
</form>
{% endif %}
</section>
{% endblock %}
</article>
{% block extra_body %}
<script type="text/javascript">
(function() {
var message = "{% trans 'Do you really want to remove the selected e-mail address?' %}";
var actions = document.getElementsByName('action_remove');
if (actions.length) {
actions[0].addEventListener("click", function(e) {
if (! confirm(message)) {
e.preventDefault();
}
});
}
})();
</script>
{% endblock %}

View File

@ -1,34 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %}
{% block content %}
<article>
<h1>{% trans "Confirm E-mail Address" %}</h1>
{% if confirmation %}
{% user_display confirmation.email_address.user as user_display %}
<p>{% blocktrans with confirmation.email_address.email as email %}Please confirm that <a href="mailto:{{ email }}">{{ email }}</a> is an e-mail address for user {{ user_display }}.{% endblocktrans %}</p>
<form method="post" action="{% url 'account_confirm_email' confirmation.key %}">
{% csrf_token %}
<p>
<button type="submit">{% trans 'Confirm' %}</button>
</p>
</form>
{% else %}
{% url 'account_email' as email_url %}
<p>{% blocktrans %}This e-mail confirmation link expired or is invalid. Please <a href="{{ email_url }}">issue a new e-mail confirmation request</a>.{% endblocktrans %}</p>
{% endif %}
</article>
{% endblock %}

View File

@ -1,9 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<article class="panel">
<section>
<p>You have been logged out. <a href="{% url 'login' %}">Log in</a></p>
</section>
</article>
{% endblock %}

View File

@ -1,22 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<article class="panel">
<h1>Log in</h1>
{% if form.errors %}
<p class="error">Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action="{% url 'account_login' %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<small><a href="{% url 'account_reset_password' %}">Forgot your password?</a></small>
</p>
<p>
<input type="submit" value="Login" class="btn">
<input type="hidden" name="next" value="{{ next }}">
</p>
</form>
</article>
{% endblock %}

View File

@ -1,18 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<article class="panel">
<h1>Log Out</h1>
<p>Are you sure you want to log out?</p>
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<input type="submit" value="Log out" class="btn">
<input type="hidden" name="next" value="{{ next }}">
</p>
</form>
</article>
{% endblock %}

View File

@ -1,33 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<article>
<header>
<p><a href="{% url 'storefront:customer-detail' user.pk %}">&larr; Back</a></p>
<h1>{% trans "Change Password" %}</h1>
<a href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
</header>
<form method="POST" action="{% url 'account_change_password' %}" class="password_change">
{% csrf_token %}
<table class="form-table">
<tbody>
{{ form.as_table }}
</tbody>
<tfoot>
<tr>
<td>
</td>
<td>
<button type="submit">Save changes</button>
</td>
</tr>
</tfoot>
</table>
</form>
</article>
{% endblock %}

View File

@ -1,12 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<article class="panel">
<section>
<p>
Password has been changed.
<a href="{% url 'login' %}" class="btn">Log in</a>
</p>
</section>
</article>
{% endblock %}

View File

@ -1,14 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<article class="panel">
<h1>Change password</h1>
<form method="post" action="{% url 'password_change' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Change my password" class="btn"> or
<a href="{{request.META.HTTP_REFERER}}">Cancel</a>
</form>
</article>
{% endblock %}

View File

@ -1,25 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Password Reset" %} | {% endblock %}
{% block content %}
<article>
<h1>{% trans "Password Reset" %}</h1>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}</p>
<form method="POST" action="{% url 'account_reset_password' %}" class="password_reset">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="{% trans 'Reset My Password' %}" />
</form>
<p>{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}</p>
</article>
{% endblock %}

View File

@ -1,24 +0,0 @@
{% extends 'base.html' %}
{% block content %}
{% if validlink %}
<article class="panel">
<h1>Reset password</h1>
<p>Enter a <em>new</em> password below.</p>
<form method="post" action=".">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Reset your password" class="btn">
</form>
</article>
{% else %}
<article class="panel">
<h1 class="error">Password reset failed</h1>
</article>
{% endif %}
{% endblock %}

View File

@ -1,18 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Password Reset" %}{% endblock %}
{% block content %}
<article>
<h1>{% trans "Password Reset" %}</h1>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>{% blocktrans %}We have sent you an e-mail. If you have not received it please check your spam folder. Otherwise contact us if you do not receive it in a few minutes.{% endblocktrans %}</p>
</article>
{% endblock %}

View File

@ -1,10 +0,0 @@
{% load i18n %}{% autoescape off %}
{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %}
{% translate "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 %}
{% translate 'Your username, in case youve forgotten:' %} {{ user.get_username }}
{% endautoescape %}

View File

@ -1,21 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<article>
<h1>{% if token_fail %}{% trans "Bad Token" %}{% else %}{% trans "Change Password" %}{% endif %}</h1>
{% if token_fail %}
{% url 'account_reset_password' as passwd_reset_url %}
<p>{% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a <a href="{{ passwd_reset_url }}">new password reset</a>.{% endblocktrans %}</p>
{% else %}
<form method="POST" action="{{ action_url }}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" name="action" value="{% trans 'change password' %}"/>
</form>
{% endif %}
</article>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<article>
<h1>{% trans "Change Password" %}</h1>
<p>{% trans 'Your password is now changed.' %}</p>
</article>
{% endblock %}

View File

@ -1,17 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<article>
<h1>Sign up</h1>
<form method="post" action="{% url 'account_signup' %}">
{% csrf_token %}
{{ form.as_p }}
<p><small>By clicking you agree to the <a href="{% url 'storefront:terms-and-conditions' %}">Terms and conditions</a> and <a href="{% url 'storefront:privacy-policy' %}">Privacy Policy</a>.</small></p>
<p>
<input type="submit" value="Create account" class="btn">
<input type="hidden" name="next" value="{{ next }}">
</p>
</form>
</article>
{% endblock %}

View File

@ -1,6 +1,5 @@
{% load static %} {% load static %}
{% load compress %} {% load compress %}
{% load account %}
{% load analytical %} {% load analytical %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -19,7 +18,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Vollkorn:wght@900&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap" rel="stylesheet">
{% compress css %} {% compress css %}
<link rel="stylesheet" type="text/css" href="{% static 'styles/normalize.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'styles/normalize.css' %}">
@ -69,14 +68,14 @@
<li><a class="nav__link" href="{% url 'dashboard:home' %}">Dashboard</a></li> <li><a class="nav__link" href="{% url 'dashboard:home' %}">Dashboard</a></li>
{% endif %} {% endif %}
<li><a class="nav__link" href="{% url 'storefront:customer-detail' user.pk %}">Orders</a></li> <li><a class="nav__link" href="{% url 'storefront:customer-detail' user.pk %}">Orders</a></li>
<li><a class="nav__link" href="{% url 'account_logout' %}">Logout</a></li> <li><a class="nav__link" href="{% url 'logout' %}">Logout</a></li>
</ul> </ul>
</div> </div>
{% else %} {% else %}
<ul class="nav__list nav__account"> <ul class="nav__list nav__account">
<li><a class="nav__link" href="{% url 'account_login' %}">Login</a></li> <li><a class="nav__link" href="{% url 'login' %}">Login</a></li>
<li>/</li> <li>/</li>
<li><a class="nav__link" href="{% url 'account_signup' %}">Signup</a></li> <li><a class="nav__link" href="{% url 'signup' %}">Signup</a></li>
</ul> </ul>
{% endif %} {% endif %}
<a class="site__cart" href="{% url 'storefront:cart-detail' %}"> <a class="site__cart" href="{% url 'storefront:cart-detail' %}">

View File

@ -15,7 +15,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;400;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap" rel="stylesheet">
{% compress css %} {% compress css %}
<link rel="stylesheet" type="text/css" href="{% static "styles/dashboard.css" %}"> <link rel="stylesheet" type="text/css" href="{% static "styles/dashboard.css" %}">
@ -65,11 +65,11 @@
<a href="{% url 'storefront:product-list' %}">Storefront</a> <a href="{% url 'storefront:product-list' %}">Storefront</a>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="account">{{ user.get_full_name }}</a> <a href="account">{{ user.get_full_name }}</a>
<a href="{% url 'account_logout' %}">Log Out</a> <a href="{% url 'logout' %}">Log Out</a>
{% else %} {% else %}
<p>You are not logged in</p> <p>You are not logged in</p>
<a href="{% url 'account_login' %}">Log In</a> | <a href="{% url 'login' %}">Log In</a> |
<a href="{% url 'account_signup' %}">Sign Up</a> <a href="{% url 'signup' %}">Sign Up</a>
{% endif %} {% endif %}
</div> </div>
</aside> </aside>