mirror of
https://github.com/AxioDL/axiodl-www.git
synced 2026-07-11 06:19:06 -07:00
Basic infrastructure work
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
*.pyc
|
||||
*~
|
||||
__pycache__
|
||||
*venv*
|
||||
db.sqlite3
|
||||
/static
|
||||
/media
|
||||
/martor
|
||||
.DS_Store
|
||||
@@ -0,0 +1,31 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import Profile
|
||||
|
||||
|
||||
class ProfileInline(admin.StackedInline):
|
||||
model = Profile
|
||||
can_delete = False
|
||||
verbose_name_plural = 'Profile'
|
||||
fk_name = 'user'
|
||||
|
||||
|
||||
class CustomUserAdmin(UserAdmin):
|
||||
inlines = (ProfileInline, )
|
||||
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'get_location')
|
||||
list_select_related = ('profile', )
|
||||
|
||||
def get_location(self, instance):
|
||||
return instance.profile.location
|
||||
get_location.short_description = 'Location'
|
||||
|
||||
def get_inline_instances(self, request, obj=None):
|
||||
if not obj:
|
||||
return list()
|
||||
return super(CustomUserAdmin, self).get_inline_instances(request, obj)
|
||||
|
||||
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User, CustomUserAdmin)
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
name = 'accounts'
|
||||
@@ -0,0 +1,50 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
from .models import Profile
|
||||
|
||||
|
||||
class SignUpForm(UserCreationForm):
|
||||
email = forms.CharField(max_length=254, required=True, widget=forms.EmailInput())
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'email', 'password1', 'password2')
|
||||
|
||||
|
||||
class ProfileForm(forms.ModelForm):
|
||||
first_name = forms.CharField(max_length=256)
|
||||
last_name = forms.CharField(max_length=256)
|
||||
email = forms.EmailField(max_length=254, required=True, widget=forms.EmailInput())
|
||||
use_gravatar = forms.BooleanField(label='Use avatars from Gravatar?', required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ProfileForm, self).__init__(*args, **kwargs)
|
||||
|
||||
try:
|
||||
self.fields['use_gravatar'].initial = self.instance.profile.use_gravatar
|
||||
self.fields['avatar'].initial = self.instance.profile.avatar
|
||||
self.fields['first_name'].initial = self.instance.first_name
|
||||
self.fields['last_name'].initial = self.instance.last_name
|
||||
self.fields['email'].initial = self.instance.email
|
||||
self.fields['location'].initial = self.instance.profile.location
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
class Meta:
|
||||
model = Profile
|
||||
fields = ['avatar', 'first_name', 'last_name', 'email', 'use_gravatar', 'location', ]
|
||||
exclude = ['user']
|
||||
|
||||
def save(self, commit=True):
|
||||
super(ProfileForm, self).save(commit)
|
||||
self.instance.profile.use_gravatar = self.cleaned_data['use_gravatar']
|
||||
if self.cleaned_data['avatar'] is not False:
|
||||
self.instance.profile.avatar = self.cleaned_data['avatar']
|
||||
else:
|
||||
self.instance.profile.avatar = None
|
||||
self.instance.profile.location = self.cleaned_data['location']
|
||||
self.instance.profile.save()
|
||||
self.instance.save()
|
||||
|
||||
return self.instance
|
||||
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 2.2.3 on 2019-07-07 12:44
|
||||
|
||||
import accounts.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Profile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('avatar', models.ImageField(blank=True, null=True, upload_to=accounts.models.path_and_rename)),
|
||||
('birthdate', models.DateTimeField(blank=True, null=True)),
|
||||
('use_gravatar', models.BooleanField(blank=True, default=False)),
|
||||
('location', models.CharField(blank=True, max_length=100, null=True)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
import os
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
def path_and_rename(instance, filename):
|
||||
upload_to = 'avatars/'
|
||||
ext = filename.split('.')[-1]
|
||||
# get filename
|
||||
if instance.pk:
|
||||
filename = '{}.{}'.format(instance.pk, ext)
|
||||
else:
|
||||
# set filename as random string
|
||||
filename = '{}.{}'.format(uuid4().hex, ext)
|
||||
# return the whole path to the file
|
||||
return os.path.join(upload_to, filename)
|
||||
|
||||
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
avatar = models.ImageField(upload_to=path_and_rename, null=True, blank=True)
|
||||
birthdate = models.DateTimeField(null=True, blank=True)
|
||||
use_gravatar = models.BooleanField(blank=True, default=False)
|
||||
location = models.CharField(max_length=100, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_or_update_user_profile(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
Profile.objects.create(user=instance)
|
||||
instance.profile.save()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
@@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<h1 class="text-center logo my-4">
|
||||
<a href="{% url 'home' %}" class="text-light">AxioDL</a>
|
||||
</h1>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,31 @@
|
||||
{% extends 'base_accounts.html' %}
|
||||
|
||||
{% block title %}Log in to AxioDL{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-4 col-md-6 col-sm-8">
|
||||
<div class="card border-dark">
|
||||
<div class="card-header bg-dark text-light">
|
||||
<div class="card-title"><h2 class="card-title">Log in</h2></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
{% include 'includes/form.html' %}
|
||||
<button type="submit" class="btn btn-primary btn-block">Log in</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-muted text-center">
|
||||
New to AxioDL? <a href="{% url 'signup' %}">Sign up</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center py-2">
|
||||
<small>
|
||||
<a href="{% url 'password_reset' %}" class="text-muted">Forgot your password?</a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,26 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Change password{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
<li class="breadcrumb-item active">Change password</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6 col-md-8 col-sm-10">
|
||||
<div class="card border-dark">
|
||||
<div class="card-header bg-dark text-light">
|
||||
<div class="card-title"><h2 class="card-title">Change your password</h2></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{% include 'includes/form.html' %}
|
||||
<button type="submit" class="btn btn-success btn-block">Change password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Change password successful{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
<li class="breadcrumb-item"><a href="{% url 'password_change' %}">Change password</a></li>
|
||||
<li class="breadcrumb-item active">Success</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="alert alert-success" role="alert">
|
||||
<strong>Success!</strong> Your password has been changed!
|
||||
</div>
|
||||
<a href="{% url 'home' %}" class="btn btn-secondary">Return to home page</a>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends 'base_accounts.html' %}
|
||||
|
||||
{% block title %}Reset your password{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-4 col-md-6 col-sm-8">
|
||||
<div class="card border-dark">
|
||||
<div class="card-header bg-dark text-light">
|
||||
<div class="card-title"><h2 class="card-title">Reset your password</h2></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Enter your email address and we will send you a link to reset your password.</p>
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{% include 'includes/form.html' %}
|
||||
<button type="submit" class="btn btn-primary btn-block">Send password reset email</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends 'base_accounts.html' %}
|
||||
|
||||
{% block title %}Password changed!{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6 col-md-8 col-sm-10">
|
||||
<div class="card border-dark">
|
||||
<div class="card-header bg-dark text-light">
|
||||
<div class="card-title"><h2 class="card-title">Password changed!</h2></div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-success" role="alert">
|
||||
You have successfully changed your password! You may now proceed to log in.
|
||||
</div>
|
||||
<a href="{% url 'login' %}" class="btn btn-secondary btn-block">Return to log in</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends 'base_accounts.html' %}
|
||||
|
||||
{% block title %}
|
||||
{% if validlink %}
|
||||
Change password for {{ form.user.username }}
|
||||
{% else %}
|
||||
Reset your password
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6 col-md-8 col-sm-10">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{% if validlink %}
|
||||
<h3 class="card-title">Change password for {{ form.user.username }}</h3>
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{% include 'includes/form.html' %}
|
||||
<button type="submit" class="btn btn-success btn-block">Change password</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<h3 class="card-title">Reset your password</h3>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
It looks like you clicked on an invalid password reset link. Please try again.
|
||||
</div>
|
||||
<a href="{% url 'password_reset' %}" class="btn btn-secondary btn-block">Request a new password reset link</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,17 @@
|
||||
{% extends 'base_accounts.html' %}
|
||||
|
||||
{% block title %}Reset your password{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-5 col-md-6 col-sm-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Reset your password</h3>
|
||||
<p>Check your email for a link to reset your password.<br />If it doesn't appear within a few minutes, check your spam folder.</p>
|
||||
<a href="{% url 'login' %}" class="btn btn-secondary btn-block">Return to log in</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
Hi there,
|
||||
|
||||
Someone asked for a password reset for the email address {{ email }}.
|
||||
Follow the link below:
|
||||
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
|
||||
|
||||
In case you forgot your AxioDL username: {{ user.username }}
|
||||
|
||||
If clicking the link above doesn't work, please copy and paste the URL
|
||||
in a new browser window instead.
|
||||
|
||||
If you've received this mail in error, it's likely that another user entered
|
||||
your email address by mistake while trying to reset a password. If you didn't
|
||||
initiate the request, you don't need to take any further action and can safely
|
||||
disregard this email.
|
||||
|
||||
Thanks,
|
||||
|
||||
The AxioDL Team
|
||||
@@ -0,0 +1 @@
|
||||
[AxioDL] Please reset your password
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends 'base_accounts.html' %}
|
||||
|
||||
{% block title %}Sign up to Django Boards{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8 col-md-10 col-sm-12">
|
||||
<div class="card border-dark">
|
||||
<div class="card-header bg-dark text-light">
|
||||
<div class="card-title"><h2 class="card-title">Sign up</h2></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{% include 'includes/form.html' %}
|
||||
<button type="submit" class="btn btn-primary btn-block">Create an account</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-muted text-center">
|
||||
Already have an account? <a href="{% url 'login' %}">Log in</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user