Added code for user management
Added templates Form rendering is fucked up
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 819 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit a0244ea92401879f1a2435616aea98fc4404bdf8
|
After Width: | Height: | Size: 434 KiB |
|
@ -0,0 +1,15 @@
|
||||||
|
@charset "utf-8"
|
||||||
|
|
||||||
|
$primary: #e60000
|
||||||
|
|
||||||
|
@import "bulma/bulma"
|
||||||
|
|
||||||
|
.rom-overview
|
||||||
|
padding: 1vw
|
||||||
|
background-image: url("background.jpg")
|
||||||
|
background-size: cover
|
||||||
|
background-repeat: no-repeat
|
||||||
|
background-attachment: fixed
|
||||||
|
background-position: center
|
||||||
|
.categorie
|
||||||
|
margin: 0.5vw
|
|
@ -0,0 +1,140 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Gulashromstore Mockup</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gulash_bulma.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="fontawsome/css/font-awesome.min.css">
|
||||||
|
|
||||||
|
<script src="jquery-3.2.1.slim.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<nav class="nav has-shadow">
|
||||||
|
<div class="container">
|
||||||
|
<div class="nav-left">
|
||||||
|
<a class="nav-item">
|
||||||
|
<img src="logo.png" />
|
||||||
|
</a>
|
||||||
|
<a class="nav-item is-tab is-hidden-mobile is-active">Roms</a>
|
||||||
|
<a class="nav-item is-tab is-hidden-mobile">Upload Rom</a>
|
||||||
|
</div>
|
||||||
|
<span class="nav-toggle">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="nav-right nav-menu">
|
||||||
|
<a class="nav-item is-tab is-hidden-tablet is-active">Roms</a>
|
||||||
|
<a class="nav-item is-tab is-hidden-tablet">Rom Hochladen</a>
|
||||||
|
|
||||||
|
<a class="nav-item is-tab">Registrieren</a>
|
||||||
|
<a class="nav-item is-tab">
|
||||||
|
Einstellungen
|
||||||
|
</a>
|
||||||
|
<a class="nav-item is-tab">Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="rom-overview">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<div class="box">
|
||||||
|
<strong>Kategorie:</strong>
|
||||||
|
<a hre=""><span class="tag is-medium categorie">Gulasch</span></a>
|
||||||
|
<a hre=""><span class="tag is-primary is-medium categorie">Blinkenlight</span></a>
|
||||||
|
<a hre=""><span class="tag is-medium categorie">1337 Haxxoring</span></a>
|
||||||
|
<a hre=""><span class="tag is-medium categorie">Random</span></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 1</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 2</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 3</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 4</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 5</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 6</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 7</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 8</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 9</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<a class="box has-text-centered">
|
||||||
|
<img src="http://placehold.it/400x400" />
|
||||||
|
<h1 class="title is-4">Awsøme R0m 10</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="content has-text-centered">
|
||||||
|
<strong>Gulasch R0MSTØRE</strong> by Sebastian 2017
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$('.nav-toggle').on('click', function(event) {
|
||||||
|
$('.nav-toggle').toggleClass('is-active');
|
||||||
|
$('.nav-menu').toggleClass('is-active');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
After Width: | Height: | Size: 43 KiB |
|
@ -58,7 +58,7 @@ ROOT_URLCONF = 'gulashromstore.urls'
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [],
|
'DIRS': ['templates'],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
|
@ -123,6 +123,10 @@ USE_TZ = True
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
|
STATICFILES_DIRS = [
|
||||||
|
os.path.join(BASE_DIR, "static"),
|
||||||
|
]
|
||||||
|
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from gulashromstore.settings import MEDIA_URL, MEDIA_ROOT
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^roms/', include('roms.urls')),
|
url(r'^roms/', include('roms.urls')),
|
||||||
|
url(r'^users/', include('users.urls')),
|
||||||
url(r'^admin/', admin.site.urls),
|
url(r'^admin/', admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,7 @@ from django.contrib import admin
|
||||||
|
|
||||||
from taggit_helpers.admin import TaggitListFilter, TaggitTabularInline
|
from taggit_helpers.admin import TaggitListFilter, TaggitTabularInline
|
||||||
|
|
||||||
from roms.models import Rom, RomFile
|
from roms.models import Rom
|
||||||
from roms.forms import RomFileForm, RomFileInlineFormSet
|
|
||||||
from gulashromstore.settings import SLOT_COUNT
|
|
||||||
|
|
||||||
|
|
||||||
class RomFileInline(admin.TabularInline):
|
|
||||||
model = RomFile
|
|
||||||
form = RomFileForm
|
|
||||||
formset = RomFileInlineFormSet
|
|
||||||
|
|
||||||
fields = ('slot', 'binary')
|
|
||||||
|
|
||||||
can_delete = False
|
|
||||||
max_num = SLOT_COUNT
|
|
||||||
min_num = SLOT_COUNT
|
|
||||||
|
|
||||||
|
|
||||||
class RomAdmin(admin.ModelAdmin):
|
class RomAdmin(admin.ModelAdmin):
|
||||||
|
@ -24,14 +10,10 @@ class RomAdmin(admin.ModelAdmin):
|
||||||
list_filter = [TaggitListFilter]
|
list_filter = [TaggitListFilter]
|
||||||
actions = ['mark_approved', 'mark_disapproved']
|
actions = ['mark_approved', 'mark_disapproved']
|
||||||
|
|
||||||
fieldsets = ((None, {'fields' : ('name', 'description', 'cover', 'approved')}),)
|
|
||||||
|
|
||||||
inlines = [
|
inlines = [
|
||||||
RomFileInline,
|
|
||||||
TaggitTabularInline
|
TaggitTabularInline
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def tag_list(self, obj):
|
def tag_list(self, obj):
|
||||||
return u", ".join(obj.tag_list())
|
return u", ".join(obj.tag_list())
|
||||||
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
from django.forms import Field, Widget, ModelForm
|
|
||||||
from django.forms.models import BaseInlineFormSet
|
|
||||||
|
|
||||||
from roms.models import RomFile
|
|
||||||
|
|
||||||
class ReadOnlyWidget(Widget):
|
|
||||||
def render(self, name, value, attrs):
|
|
||||||
final_attrs = self.build_attrs(attrs, name=name)
|
|
||||||
if hasattr(self, 'initial'):
|
|
||||||
value = self.initial
|
|
||||||
return "%s" % (value or '')
|
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class ReadOnlyField(Field):
|
|
||||||
widget = ReadOnlyWidget
|
|
||||||
def __init__(self, widget=None, label=None, initial=None, help_text=None):
|
|
||||||
super(ReadOnlyField, self).__init__(self, label=label, initial=initial,
|
|
||||||
help_text=help_text, widget=widget)
|
|
||||||
|
|
||||||
def clean(self, value, initial):
|
|
||||||
self.widget.initial = initial
|
|
||||||
return initial
|
|
||||||
|
|
||||||
|
|
||||||
class RomFileForm(ModelForm):
|
|
||||||
slot = ReadOnlyField()
|
|
||||||
class Meta:
|
|
||||||
model = RomFile
|
|
||||||
fields = ['binary']
|
|
||||||
|
|
||||||
|
|
||||||
class RomFileInlineFormSet(BaseInlineFormSet):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(RomFileInlineFormSet, self).__init__(*args, **kwargs)
|
|
||||||
# Check that the data doesn't already exist
|
|
||||||
if not kwargs['instance'].romfile_set.all():
|
|
||||||
self.initial = [{'slot' : i} for i in range(1, SLOT_COUNT+1)]
|
|
||||||
self.extra = SLOT_COUNT
|
|
|
@ -1,9 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by Django 1.10.5 on 2017-03-18 17:30
|
# Generated by Django 1.10.6 on 2017-04-28 17:32
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import roms.models
|
||||||
import taggit.managers
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,22 +22,10 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('name', models.CharField(max_length=128, verbose_name='name')),
|
('name', models.CharField(max_length=128, verbose_name='name')),
|
||||||
('description', models.TextField(verbose_name='description')),
|
('description', models.TextField(verbose_name='description')),
|
||||||
('cover', models.ImageField(upload_to='', verbose_name='cover image')),
|
('cover', models.ImageField(upload_to=roms.models.upload_cover_to, verbose_name='cover image')),
|
||||||
|
('binary', models.FileField(upload_to=roms.models.upload_binary_to, verbose_name='binary')),
|
||||||
('approved', models.BooleanField(verbose_name='approved')),
|
('approved', models.BooleanField(verbose_name='approved')),
|
||||||
('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
|
('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
|
||||||
name='RomFile',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('slot', models.IntegerField(choices=[(1, 'Slot 1'), (2, 'Slot 2'), (3, 'Slot 3'), (4, 'Slot 4'), (5, 'Slot 5'), (6, 'Slot 6')], verbose_name='slot')),
|
|
||||||
('binary', models.FileField(upload_to='', verbose_name='binary')),
|
|
||||||
('rom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='roms.Rom', verbose_name='rom')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='romfile',
|
|
||||||
unique_together=set([('rom', 'slot')]),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,8 +6,6 @@ from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from gulashromstore.settings import SLOT_COUNT
|
|
||||||
|
|
||||||
def upload_cover_to(instance, filename):
|
def upload_cover_to(instance, filename):
|
||||||
_, ext = os.path.splitext(filename)
|
_, ext = os.path.splitext(filename)
|
||||||
return "covers/%s.%s" % (uuid.uuid4(), ext)
|
return "covers/%s.%s" % (uuid.uuid4(), ext)
|
||||||
|
@ -20,10 +18,10 @@ class Rom(models.Model):
|
||||||
name = models.CharField("name", max_length = 128)
|
name = models.CharField("name", max_length = 128)
|
||||||
description = models.TextField("description")
|
description = models.TextField("description")
|
||||||
cover = models.ImageField("cover image", upload_to=upload_cover_to)
|
cover = models.ImageField("cover image", upload_to=upload_cover_to)
|
||||||
|
binary = models.FileField("binary", upload_to=upload_binary_to)
|
||||||
approved = models.BooleanField("approved")
|
approved = models.BooleanField("approved")
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
|
|
||||||
def tag_list(self):
|
def tag_list(self):
|
||||||
return [t.name for t in self.tags.all()]
|
return [t.name for t in self.tags.all()]
|
||||||
|
|
||||||
|
@ -33,24 +31,11 @@ class Rom(models.Model):
|
||||||
'name' : self.name,
|
'name' : self.name,
|
||||||
'description' : self.description,
|
'description' : self.description,
|
||||||
'tags' : self.tag_list(),
|
'tags' : self.tag_list(),
|
||||||
'roms' : {romfile.slot : romfile.binary.url for romfile in self.romfile_set.all()}
|
'cover' : self.cover,
|
||||||
|
'binary' : self.binary
|
||||||
}
|
}
|
||||||
|
|
||||||
return json
|
return json
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Rom %s" % self.name
|
return "Rom %s" % self.name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RomFile(models.Model):
|
|
||||||
SLOT_CHOICES = [(i, "Slot %d" % i) for i in range(1, SLOT_COUNT+1)]
|
|
||||||
rom = models.ForeignKey(Rom, verbose_name = "rom")
|
|
||||||
slot = models.IntegerField("slot", choices=SLOT_CHOICES)
|
|
||||||
binary = models.FileField("binary", upload_to=upload_binary_to)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ('rom', 'slot')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "RomFile %s %d" % (self.rom.name, self.slot)
|
|
||||||
|
|
After Width: | Height: | Size: 434 KiB |
After Width: | Height: | Size: 819 KiB |
After Width: | Height: | Size: 43 KiB |
|
@ -0,0 +1,62 @@
|
||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Gulashromstore Mockup</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "css/gulash_bulma.css" %}">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "fontawsome/css/font-awesome.min.css" %}">
|
||||||
|
|
||||||
|
<script src="{% static "jquery-3.2.1.slim.min.js" %}"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<nav class="nav has-shadow">
|
||||||
|
<div class="container">
|
||||||
|
<div class="nav-left">
|
||||||
|
<a class="nav-item">
|
||||||
|
<img src="{% static "images/logo.png" %}"/>
|
||||||
|
</a>
|
||||||
|
<a class="nav-item is-tab is-hidden-mobile is-active">Roms</a>
|
||||||
|
<a class="nav-item is-tab is-hidden-mobile">Upload Rom</a>
|
||||||
|
</div>
|
||||||
|
<span class="nav-toggle">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="nav-right nav-menu">
|
||||||
|
<a class="nav-item is-tab is-hidden-tablet is-active">Roms</a>
|
||||||
|
<a class="nav-item is-tab is-hidden-tablet">Rom Hochladen</a>
|
||||||
|
|
||||||
|
<a class="nav-item is-tab">Registrieren</a>
|
||||||
|
<a class="nav-item is-tab">
|
||||||
|
Einstellungen
|
||||||
|
</a>
|
||||||
|
<a class="nav-item is-tab">Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="content has-text-centered">
|
||||||
|
<strong>Gulasch R0MSTØRE</strong> by Sebastian 2017
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$('.nav-toggle').on('click', function(event) {
|
||||||
|
$('.nav-toggle').toggleClass('is-active');
|
||||||
|
$('.nav-menu').toggleClass('is-active');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<form {% if form_enctype %}enctype="{{form_enctype}}"{% endif %} method="post" class="pure-form pure-form-aligned">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{% for field in form %}
|
||||||
|
<div class="field is-danger">
|
||||||
|
<label class="label" for="{{field.name}}">{{field.label}}</label>
|
||||||
|
<p class="control">
|
||||||
|
{{ field }}
|
||||||
|
</p>
|
||||||
|
<p class="help is-danger">
|
||||||
|
{{ field.errors.as_text}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<p class="control">
|
||||||
|
<button class="button is-primary">{{button_text}}</button>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
|
@ -0,0 +1,101 @@
|
||||||
|
from django import forms
|
||||||
|
from django.forms import ModelForm
|
||||||
|
|
||||||
|
from users.models import User
|
||||||
|
|
||||||
|
class UserCreateForm(ModelForm):
|
||||||
|
password1 = forms.CharField(label='Password', widget=forms.PasswordInput(attrs={'class': 'input'}))
|
||||||
|
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput(attrs={'class': 'input'}))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['username', 'email', 'twitter', 'github']
|
||||||
|
widgets = {
|
||||||
|
'username' : forms.TextInput(attrs={'class': 'input'}),
|
||||||
|
'email' : forms.TextInput(attrs={'class': 'input'}),
|
||||||
|
'twitter' : forms.TextInput(attrs={'class': 'input'}),
|
||||||
|
'github' : forms.TextInput(attrs={'class': 'input'}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def clean_password2(self):
|
||||||
|
password1 = self.cleaned_data.get("password1")
|
||||||
|
password2 = self.cleaned_data.get("password2")
|
||||||
|
|
||||||
|
if password1 and password2 and password1 != password2:
|
||||||
|
raise forms.ValidationError("Passwords don't match")
|
||||||
|
|
||||||
|
return password1
|
||||||
|
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
#Make sure Email is unique
|
||||||
|
email = self.cleaned_data.get("email")
|
||||||
|
if User.objects.filter(email = email):
|
||||||
|
raise forms.ValidationError("Email already in use.")
|
||||||
|
|
||||||
|
return email
|
||||||
|
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
user = super(UserCreateForm, self).save(commit=False)
|
||||||
|
user.set_password(self.cleaned_data["password1"])
|
||||||
|
user.is_active = False
|
||||||
|
if commit:
|
||||||
|
user.save()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UserUpdateForm(ModelForm):
|
||||||
|
new_password1 = forms.CharField(label='New Password',
|
||||||
|
widget=forms.PasswordInput,
|
||||||
|
required=False)
|
||||||
|
new_password2 = forms.CharField(label='New Password confirmation',
|
||||||
|
widget=forms.PasswordInput,
|
||||||
|
required=False)
|
||||||
|
current_password = forms.CharField(label='Current Password', widget=forms.PasswordInput)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['email', 'twitter', 'github']
|
||||||
|
|
||||||
|
|
||||||
|
def clean_current_password(self):
|
||||||
|
current_password = self.cleaned_data.get("current_password")
|
||||||
|
|
||||||
|
if not self.instance.check_password(current_password):
|
||||||
|
raise forms.ValidationError("Password incorrect")
|
||||||
|
|
||||||
|
return current_password
|
||||||
|
|
||||||
|
|
||||||
|
def clean_new_password2(self):
|
||||||
|
password1 = self.cleaned_data.get("new_password1")
|
||||||
|
password2 = self.cleaned_data.get("new_password2")
|
||||||
|
|
||||||
|
if password1 and password2 and password1 != password2:
|
||||||
|
raise forms.ValidationError("Passwords don't match")
|
||||||
|
|
||||||
|
return password1
|
||||||
|
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
#Make sure Email is still unique
|
||||||
|
email = self.cleaned_data.get("email")
|
||||||
|
if User.objects.filter(email = email).exclude(id=self.instance.id):
|
||||||
|
raise forms.ValidationError("Email already in use.")
|
||||||
|
|
||||||
|
return email
|
||||||
|
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
user = super(UserUpdateForm, self).save(commit=False)
|
||||||
|
|
||||||
|
password = self.cleaned_data.get("new_password1")
|
||||||
|
if password:
|
||||||
|
user.set_password(password)
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
user.save()
|
||||||
|
return user
|
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.6 on 2017-04-28 17:43
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='twitter',
|
||||||
|
field=models.CharField(blank=True, max_length=128, verbose_name='twitter handle'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='github',
|
||||||
|
field=models.CharField(blank=True, max_length=128, verbose_name='github handle'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,4 +2,5 @@ from django.db import models
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
github = models.CharField('github handle', max_length=128)
|
github = models.CharField('github handle', max_length=128, blank=True)
|
||||||
|
twitter = models.CharField('twitter handle', max_length=128, blank=True)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block title %} Sign up{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="rom-overview">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<div class="box">
|
||||||
|
<h1 class="title is-1">Registrieren</h1>
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
You are alerady logged in, why in the world would you want to sign up ?
|
||||||
|
{% else %}
|
||||||
|
{% include 'form.html' with button_text='sign up' %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,30 @@
|
||||||
|
from django.conf.urls import include, url
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
|
|
||||||
|
from django.contrib.auth.views import password_reset, password_reset_confirm, password_reset_done, login, logout
|
||||||
|
|
||||||
|
|
||||||
|
from users.views import *
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^signup/$', UserCreateView.as_view(), name='signup'),
|
||||||
|
url(r'^confirm/(?P<user_id>\d+)/$', SendConfirmationView.as_view(), name='send_confirmation'),
|
||||||
|
url(r'^confirm/(?P<user_id>\d+)/(?P<token>.+)/$', CheckConfirmationView.as_view(), name='check_confirmation'),
|
||||||
|
|
||||||
|
url(r'^update/(?P<user_id>\d+)/$', UserUpdateView.as_view(), name='user_update'),
|
||||||
|
|
||||||
|
url(r'^password/reset/$', password_reset, {'template_name' : 'users/password_reset.html',
|
||||||
|
'post_reset_redirect' : reverse_lazy('password_reset_sent')},
|
||||||
|
name='password_reset'),
|
||||||
|
|
||||||
|
url(r'^password/reset/(?P<uidb36>[0-9A-Za-z]+)/(?P<token>.+)/$', password_reset_confirm,
|
||||||
|
{'template_name' : 'users/password_reset_confirm.html',
|
||||||
|
'post_reset_redirect' : reverse_lazy('login')},
|
||||||
|
name='password_reset_confirm'),
|
||||||
|
|
||||||
|
url(r'^password/reset/sent/$', password_reset_done, { 'template_name' : 'users/password_reset_sent.html',},
|
||||||
|
name='password_reset_sent'),
|
||||||
|
|
||||||
|
url(r'^login/$', login, {'template_name' : 'users/login.html'}, name='login'),
|
||||||
|
url(r'^logout/$', logout ,{'next_page' : reverse_lazy('login')}, name='logout'),
|
||||||
|
]
|
100
users/views.py
|
@ -1,3 +1,99 @@
|
||||||
from django.shortcuts import render
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
|
from django.views.generic.base import TemplateView
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django.template import loader
|
||||||
|
from django.utils.http import int_to_base36
|
||||||
|
from django.http import Http404
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
# Create your views here.
|
from users.forms import UserCreateForm, UserUpdateForm
|
||||||
|
|
||||||
|
class UserCreateView(CreateView):
|
||||||
|
model = User
|
||||||
|
form_class = UserCreateForm
|
||||||
|
template_name = 'users/signup.html'
|
||||||
|
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('send_confirmation', kwargs={'user_id' : self.object.pk})
|
||||||
|
|
||||||
|
|
||||||
|
class UserUpdateView(UpdateView):
|
||||||
|
model = User
|
||||||
|
form_class = UserUpdateForm
|
||||||
|
template_name = 'users/user_update.html'
|
||||||
|
pk_url_kwarg = 'user_id'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.model.objects.filter(id=self.request.user.id)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('geometry_create')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SendConfirmationView(TemplateView):
|
||||||
|
template_name = 'users/send_confirmation.html'
|
||||||
|
email_template_name = 'users/confirmation_email.txt'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(SendConfirmationView, self).get_context_data(**kwargs)
|
||||||
|
context['confirm_user'] = User.objects.get(id=kwargs['user_id'])
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
user = User.objects.get(id=kwargs['user_id'])
|
||||||
|
except User.DoesNotExist:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
if user.is_active:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
uid = int_to_base36(user.pk)
|
||||||
|
token = default_token_generator.make_token(user)
|
||||||
|
link = request.build_absolute_uri(reverse('check_confirmation', kwargs={'user_id' : user.pk, 'token' : token}))
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'email': user.email,
|
||||||
|
'validation_link': link,
|
||||||
|
'user': user
|
||||||
|
}
|
||||||
|
|
||||||
|
subject = "Validate your registration at %s" % site_name
|
||||||
|
email = loader.render_to_string(self.email_template_name, context)
|
||||||
|
user.email_user(subject,email)
|
||||||
|
|
||||||
|
|
||||||
|
return super(SendConfirmationView,self).get(self, request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CheckConfirmationView(TemplateView):
|
||||||
|
template_name = 'users/check_confirmation.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(CheckConfirmationView, self).get_context_data(**kwargs)
|
||||||
|
context['confirm_user'] = User.objects.get(id=kwargs['user_id'])
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
user = User.objects.get(id=kwargs['user_id'])
|
||||||
|
except User.DoesNotExist:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
if user.is_active:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
if not default_token_generator.check_token(user,kwargs['token']):
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
user.is_active = True
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
print("Acitvating %s" % user.username)
|
||||||
|
|
||||||
|
return super(CheckConfirmationView,self).get(self, request, *args, **kwargs)
|
||||||
|
|