Skip to content

Commit

Permalink
Merge pull request #21 from rdmorganiser/accounts
Browse files Browse the repository at this point in the history
Accounts
  • Loading branch information
jochenklar authored Nov 30, 2016
2 parents bfe0736 + fdc6046 commit d7ff18b
Show file tree
Hide file tree
Showing 84 changed files with 1,756 additions and 1,028 deletions.
13 changes: 7 additions & 6 deletions apps/accounts/admin.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from django.contrib import admin

from .models import DetailKey, Profile
from .models import AdditionalField, AdditionalFieldValue


class DetailKeyAdmin(admin.ModelAdmin):
class AdditionalFieldAdmin(admin.ModelAdmin):
pass


class ProfileAdmin(admin.ModelAdmin):
readonly_fields = ('user', )
class AdditionalFieldValueAdmin(admin.ModelAdmin):
pass


admin.site.register(DetailKey, DetailKeyAdmin)
admin.site.register(Profile, ProfileAdmin)
admin.site.register(AdditionalField, AdditionalFieldAdmin)
admin.site.register(AdditionalFieldValue, AdditionalFieldValueAdmin)
77 changes: 49 additions & 28 deletions apps/accounts/forms.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,63 @@
from django import forms
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

from .models import AdditionalField, AdditionalFieldValue


class ProfileForm(forms.ModelForm):

class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
fields = ('first_name', 'last_name')


class ProfileForm(forms.Form):
def __init__(self, *args, **kwargs):
profile = kwargs.pop('profile')
detail_keys = kwargs.pop('detail_keys')

super(ProfileForm, self).__init__(*args, **kwargs)

self.fields['first_name'].widget = forms.TextInput(attrs={'placeholder': _('First name')})
self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': _('Last name')})

self.additional_fields = AdditionalField.objects.all()
self.additional_values = self.instance.additional_values.all()

# add fields and init values for the Profile model
for detail_key in detail_keys:
if detail_key.type == 'text':
field = forms.CharField()
elif detail_key.type == 'textarea':
field = forms.CharField(widget=forms.Textarea)
elif detail_key.type == 'select':
field = forms.ChoiceField(choices=detail_key.options)
elif detail_key.type == 'radio':
field = forms.ChoiceField(choices=detail_key.options, widget=forms.RadioSelect)
elif detail_key.type == 'multiselect':
field = forms.MultipleChoiceField(choices=detail_key.options)
elif detail_key.type == 'checkbox':
field = forms.MultipleChoiceField(choices=detail_key.options, widget=forms.CheckboxSelectMultiple)
for additional_field in self.additional_fields:

if additional_field.type == 'text':
field = forms.CharField(widget=forms.TextInput(attrs={'placeholder': additional_field.text}))
elif additional_field.type == 'textarea':
field = forms.CharField(widget=forms.Textarea(attrs={'placeholder': additional_field.text}))
else:
raise Exception('Unknown detail key type.')
raise Exception('Unknown additional_field type.')

field.label = additional_field.text
field.help = additional_field.help
field.required = additional_field.required

self.fields[additional_field.key] = field

for additional_field_value in self.additional_values:
self.fields[additional_field.key].initial = additional_field_value.value

def save(self, *args, **kwargs):
super(ProfileForm, self).save(*args, **kwargs)
self._save_additional_values()

def _save_additional_values(self, user=None):
if user is None:
user = self.instance

for additional_field in self.additional_fields:
try:
additional_value = user.additional_values.get(field=additional_field)
except AdditionalFieldValue.DoesNotExist:
additional_value = AdditionalFieldValue(user=user, field=additional_field)

additional_value.value = self.cleaned_data[additional_field.key]
additional_value.save()


field.label = detail_key.label
field.required = detail_key.required
field.help_text = detail_key.help_text
self.fields[detail_key.key] = field
class SignupForm(ProfileForm):

# add an initial value, if one is found in the user details
if profile.details and detail_key.key in profile.details:
self.fields[detail_key.key].initial = profile.details[detail_key.key]
def signup(self, request, user):
self._save_additional_values(user)
62 changes: 62 additions & 0 deletions apps/accounts/migrations/0007_additional_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-11-14 12:13
from __future__ import unicode_literals

import apps.core.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0006_permissions_removed'),
]

operations = [
migrations.CreateModel(
name='AdditionalField',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.SlugField()),
('type', models.CharField(choices=[('text', 'Text'), ('textarea', 'Textarea')], max_length=11)),
('text_en', models.CharField(max_length=256)),
('text_de', models.CharField(max_length=256)),
('help_en', models.TextField(blank=True, help_text='Enter a help text to be displayed next to the input element', null=True)),
('help_de', models.TextField(blank=True, help_text='Enter a help text to be displayed next to the input element', null=True)),
('required', models.BooleanField()),
],
options={
'ordering': ('key',),
'verbose_name': 'Additional field',
'verbose_name_plural': 'Additional fields',
},
bases=(models.Model, apps.core.models.TranslationMixin),
),
migrations.CreateModel(
name='AdditionalFieldValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.CharField(max_length=256)),
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='accounts.AdditionalField')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='additional_fields', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('user', 'field'),
'verbose_name': 'Additional field value',
'verbose_name_plural': 'Additional field values',
},
),
migrations.DeleteModel(
name='DetailKey',
),
migrations.RemoveField(
model_name='profile',
name='user',
),
migrations.DeleteModel(
name='Profile',
),
]
22 changes: 22 additions & 0 deletions apps/accounts/migrations/0008_related_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-11-15 14:51
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('accounts', '0007_additional_fields'),
]

operations = [
migrations.AlterField(
model_name='additionalfieldvalue',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='additional_values', to=settings.AUTH_USER_MODEL),
),
]
85 changes: 34 additions & 51 deletions apps/accounts/models.py
Original file line number Diff line number Diff line change
@@ -1,79 +1,62 @@
from __future__ import unicode_literals

from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

from jsonfield import JSONField
from apps.core.models import TranslationMixin


@python_2_unicode_compatible
class Profile(models.Model):
user = models.OneToOneField(User)
details = JSONField(null=True, blank=True)

class Meta:
ordering = ('user',)

verbose_name = _('Profile')
verbose_name_plural = _('Profiles')

def __str__(self):
return self.user.username

@property
def full_name(self):
if self.user.first_name and self.user.last_name:
return '%s %s' % (self.user.first_name, self.user.last_name)
else:
return self.user.username

def as_dl(self):
html = '<dl>'
html += '<dt>%s</dt><dd>%s</dd>' % ('Name', self.full_name)
for detail_key in DetailKey.objects.all():
if self.details and detail_key.key in self.details:
html += '<dt>%s</dt><dd>%s</dd>' % (detail_key.key.upper(), self.details[detail_key.key])
html += '</dl>'
return html


@python_2_unicode_compatible
class DetailKey(models.Model):
class AdditionalField(models.Model, TranslationMixin):

TYPE_CHOICES = (
('text', 'Text'),
('textarea', 'Textarea'),
('checkbox', 'Checkbox'),
('radio', 'Radio button'),
('select', 'Select'),
('multiselect', 'Multiselect'),
)

key = models.SlugField()
label = models.CharField(max_length=256)
type = models.CharField(max_length=11, choices=TYPE_CHOICES)
help_text = models.TextField(blank=True, help_text=_('Enter a help text to be displayed next to the input element'))
options = JSONField(null=True, blank=True, help_text=_('Enter valid JSON of the form [[key, label], [key, label], ...]'))

text_en = models.CharField(max_length=256)
text_de = models.CharField(max_length=256)

help_en = models.TextField(null=True, blank=True, help_text=_('Enter a help text to be displayed next to the input element'))
help_de = models.TextField(null=True, blank=True, help_text=_('Enter a help text to be displayed next to the input element'))

required = models.BooleanField()

class Meta:
ordering = ('key',)

verbose_name = _('DetailKey')
verbose_name_plural = _('DetailKeys')
verbose_name = _('Additional field')
verbose_name_plural = _('Additional fields')

def __str__(self):
return self.key
return self.text

@property
def text(self):
return self.trans('text')

@property
def help(self):
return self.trans('help')


def create_profile_for_user(sender, **kwargs):
user = kwargs['instance']
if kwargs['created'] and not kwargs.get('raw', False):
profile = Profile()
profile.user = user
profile.save()
@python_2_unicode_compatible
class AdditionalFieldValue(models.Model):

post_save.connect(create_profile_for_user, sender=User)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='additional_values')
field = models.ForeignKey(AdditionalField, on_delete=models.CASCADE, related_name='+')
value = models.CharField(max_length=256)

class Meta:
ordering = ('user', 'field')

verbose_name = _('Additional field value')
verbose_name_plural = _('Additional field values')

def __str__(self):
return self.user.username + '/' + self.field.key
82 changes: 82 additions & 0 deletions apps/accounts/templates/account/email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{% extends 'core/page.html' %}
{% load i18n %}

{% block page %}

<h1>{% trans "E-mail Addresses" %}</h1>

{% if user.emailaddress_set.all %}

<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p>

<form class="email-form" action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %}

<fieldset>

{% for emailaddress in user.emailaddress_set.all %}

<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
<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}}"/>

{{ emailaddress.email }}

<div class="pull-right">
{% if emailaddress.primary %}
<span class="label label-primary">{% trans "Primary" %}</span>
{% endif %}

{% if emailaddress.verified %}
<span class="label label-success">{% trans "Verified" %}</span>
{% else %}
<span class="label label-warning">{% trans "Unverified" %}</span>
{% endif %}
</div>
</label>

{% endfor %}

<div class="email-form-buttons">
<button class="btn btn-default" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
<button class="btn btn-default" type="submit" name="action_send" >{% trans 'Re-send Verification' %}</button>
<button class="btn btn-primary" 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 %}

<h2>{% trans "Add E-mail Address" %}</h2>

<form method="post" action="{% url 'account_email' %}" class="add_email">
{% csrf_token %}

{% include 'core/bootstrap_form_fields.html' %}

<button class="btn btn-default" name="action_add" type="submit">{% trans "Add E-mail" %}</button>
</form>

{% endblock %}

{% 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 %}
Loading

0 comments on commit d7ff18b

Please sign in to comment.