Skip to content

Commit d7ff18b

Browse files
authored
Merge pull request #21 from rdmorganiser/accounts
Accounts
2 parents bfe0736 + fdc6046 commit d7ff18b

File tree

84 files changed

+1756
-1028
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+1756
-1028
lines changed

apps/accounts/admin.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
from django.contrib import admin
22

3-
from .models import DetailKey, Profile
3+
from .models import AdditionalField, AdditionalFieldValue
44

55

6-
class DetailKeyAdmin(admin.ModelAdmin):
6+
class AdditionalFieldAdmin(admin.ModelAdmin):
77
pass
88

99

10-
class ProfileAdmin(admin.ModelAdmin):
11-
readonly_fields = ('user', )
10+
class AdditionalFieldValueAdmin(admin.ModelAdmin):
11+
pass
12+
1213

13-
admin.site.register(DetailKey, DetailKeyAdmin)
14-
admin.site.register(Profile, ProfileAdmin)
14+
admin.site.register(AdditionalField, AdditionalFieldAdmin)
15+
admin.site.register(AdditionalFieldValue, AdditionalFieldValueAdmin)

apps/accounts/forms.py

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,63 @@
11
from django import forms
22
from django.contrib.auth.models import User
3+
from django.utils.translation import ugettext_lazy as _
34

5+
from .models import AdditionalField, AdditionalFieldValue
6+
7+
8+
class ProfileForm(forms.ModelForm):
49

5-
class UserForm(forms.ModelForm):
610
class Meta:
711
model = User
8-
fields = ('first_name', 'last_name', 'email')
12+
fields = ('first_name', 'last_name')
913

10-
11-
class ProfileForm(forms.Form):
1214
def __init__(self, *args, **kwargs):
13-
profile = kwargs.pop('profile')
14-
detail_keys = kwargs.pop('detail_keys')
15-
1615
super(ProfileForm, self).__init__(*args, **kwargs)
1716

17+
self.fields['first_name'].widget = forms.TextInput(attrs={'placeholder': _('First name')})
18+
self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': _('Last name')})
19+
20+
self.additional_fields = AdditionalField.objects.all()
21+
self.additional_values = self.instance.additional_values.all()
22+
1823
# add fields and init values for the Profile model
19-
for detail_key in detail_keys:
20-
if detail_key.type == 'text':
21-
field = forms.CharField()
22-
elif detail_key.type == 'textarea':
23-
field = forms.CharField(widget=forms.Textarea)
24-
elif detail_key.type == 'select':
25-
field = forms.ChoiceField(choices=detail_key.options)
26-
elif detail_key.type == 'radio':
27-
field = forms.ChoiceField(choices=detail_key.options, widget=forms.RadioSelect)
28-
elif detail_key.type == 'multiselect':
29-
field = forms.MultipleChoiceField(choices=detail_key.options)
30-
elif detail_key.type == 'checkbox':
31-
field = forms.MultipleChoiceField(choices=detail_key.options, widget=forms.CheckboxSelectMultiple)
24+
for additional_field in self.additional_fields:
25+
26+
if additional_field.type == 'text':
27+
field = forms.CharField(widget=forms.TextInput(attrs={'placeholder': additional_field.text}))
28+
elif additional_field.type == 'textarea':
29+
field = forms.CharField(widget=forms.Textarea(attrs={'placeholder': additional_field.text}))
3230
else:
33-
raise Exception('Unknown detail key type.')
31+
raise Exception('Unknown additional_field type.')
32+
33+
field.label = additional_field.text
34+
field.help = additional_field.help
35+
field.required = additional_field.required
36+
37+
self.fields[additional_field.key] = field
38+
39+
for additional_field_value in self.additional_values:
40+
self.fields[additional_field.key].initial = additional_field_value.value
41+
42+
def save(self, *args, **kwargs):
43+
super(ProfileForm, self).save(*args, **kwargs)
44+
self._save_additional_values()
45+
46+
def _save_additional_values(self, user=None):
47+
if user is None:
48+
user = self.instance
49+
50+
for additional_field in self.additional_fields:
51+
try:
52+
additional_value = user.additional_values.get(field=additional_field)
53+
except AdditionalFieldValue.DoesNotExist:
54+
additional_value = AdditionalFieldValue(user=user, field=additional_field)
55+
56+
additional_value.value = self.cleaned_data[additional_field.key]
57+
additional_value.save()
58+
3459

35-
field.label = detail_key.label
36-
field.required = detail_key.required
37-
field.help_text = detail_key.help_text
38-
self.fields[detail_key.key] = field
60+
class SignupForm(ProfileForm):
3961

40-
# add an initial value, if one is found in the user details
41-
if profile.details and detail_key.key in profile.details:
42-
self.fields[detail_key.key].initial = profile.details[detail_key.key]
62+
def signup(self, request, user):
63+
self._save_additional_values(user)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9 on 2016-11-14 12:13
3+
from __future__ import unicode_literals
4+
5+
import apps.core.models
6+
from django.conf import settings
7+
from django.db import migrations, models
8+
import django.db.models.deletion
9+
10+
11+
class Migration(migrations.Migration):
12+
13+
dependencies = [
14+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15+
('accounts', '0006_permissions_removed'),
16+
]
17+
18+
operations = [
19+
migrations.CreateModel(
20+
name='AdditionalField',
21+
fields=[
22+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23+
('key', models.SlugField()),
24+
('type', models.CharField(choices=[('text', 'Text'), ('textarea', 'Textarea')], max_length=11)),
25+
('text_en', models.CharField(max_length=256)),
26+
('text_de', models.CharField(max_length=256)),
27+
('help_en', models.TextField(blank=True, help_text='Enter a help text to be displayed next to the input element', null=True)),
28+
('help_de', models.TextField(blank=True, help_text='Enter a help text to be displayed next to the input element', null=True)),
29+
('required', models.BooleanField()),
30+
],
31+
options={
32+
'ordering': ('key',),
33+
'verbose_name': 'Additional field',
34+
'verbose_name_plural': 'Additional fields',
35+
},
36+
bases=(models.Model, apps.core.models.TranslationMixin),
37+
),
38+
migrations.CreateModel(
39+
name='AdditionalFieldValue',
40+
fields=[
41+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
42+
('value', models.CharField(max_length=256)),
43+
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='accounts.AdditionalField')),
44+
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='additional_fields', to=settings.AUTH_USER_MODEL)),
45+
],
46+
options={
47+
'ordering': ('user', 'field'),
48+
'verbose_name': 'Additional field value',
49+
'verbose_name_plural': 'Additional field values',
50+
},
51+
),
52+
migrations.DeleteModel(
53+
name='DetailKey',
54+
),
55+
migrations.RemoveField(
56+
model_name='profile',
57+
name='user',
58+
),
59+
migrations.DeleteModel(
60+
name='Profile',
61+
),
62+
]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9 on 2016-11-15 14:51
3+
from __future__ import unicode_literals
4+
5+
from django.conf import settings
6+
from django.db import migrations, models
7+
import django.db.models.deletion
8+
9+
10+
class Migration(migrations.Migration):
11+
12+
dependencies = [
13+
('accounts', '0007_additional_fields'),
14+
]
15+
16+
operations = [
17+
migrations.AlterField(
18+
model_name='additionalfieldvalue',
19+
name='user',
20+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='additional_values', to=settings.AUTH_USER_MODEL),
21+
),
22+
]

apps/accounts/models.py

Lines changed: 34 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,62 @@
11
from __future__ import unicode_literals
22

33
from django.db import models
4-
from django.db.models.signals import post_save
54
from django.contrib.auth.models import User
65
from django.utils.encoding import python_2_unicode_compatible
76
from django.utils.translation import ugettext_lazy as _
87

9-
from jsonfield import JSONField
8+
from apps.core.models import TranslationMixin
109

1110

1211
@python_2_unicode_compatible
13-
class Profile(models.Model):
14-
user = models.OneToOneField(User)
15-
details = JSONField(null=True, blank=True)
16-
17-
class Meta:
18-
ordering = ('user',)
19-
20-
verbose_name = _('Profile')
21-
verbose_name_plural = _('Profiles')
22-
23-
def __str__(self):
24-
return self.user.username
25-
26-
@property
27-
def full_name(self):
28-
if self.user.first_name and self.user.last_name:
29-
return '%s %s' % (self.user.first_name, self.user.last_name)
30-
else:
31-
return self.user.username
32-
33-
def as_dl(self):
34-
html = '<dl>'
35-
html += '<dt>%s</dt><dd>%s</dd>' % ('Name', self.full_name)
36-
for detail_key in DetailKey.objects.all():
37-
if self.details and detail_key.key in self.details:
38-
html += '<dt>%s</dt><dd>%s</dd>' % (detail_key.key.upper(), self.details[detail_key.key])
39-
html += '</dl>'
40-
return html
41-
42-
43-
@python_2_unicode_compatible
44-
class DetailKey(models.Model):
12+
class AdditionalField(models.Model, TranslationMixin):
4513

4614
TYPE_CHOICES = (
4715
('text', 'Text'),
4816
('textarea', 'Textarea'),
49-
('checkbox', 'Checkbox'),
50-
('radio', 'Radio button'),
51-
('select', 'Select'),
52-
('multiselect', 'Multiselect'),
5317
)
5418

5519
key = models.SlugField()
56-
label = models.CharField(max_length=256)
5720
type = models.CharField(max_length=11, choices=TYPE_CHOICES)
58-
help_text = models.TextField(blank=True, help_text=_('Enter a help text to be displayed next to the input element'))
59-
options = JSONField(null=True, blank=True, help_text=_('Enter valid JSON of the form [[key, label], [key, label], ...]'))
21+
22+
text_en = models.CharField(max_length=256)
23+
text_de = models.CharField(max_length=256)
24+
25+
help_en = models.TextField(null=True, blank=True, help_text=_('Enter a help text to be displayed next to the input element'))
26+
help_de = models.TextField(null=True, blank=True, help_text=_('Enter a help text to be displayed next to the input element'))
27+
6028
required = models.BooleanField()
6129

6230
class Meta:
6331
ordering = ('key',)
6432

65-
verbose_name = _('DetailKey')
66-
verbose_name_plural = _('DetailKeys')
33+
verbose_name = _('Additional field')
34+
verbose_name_plural = _('Additional fields')
6735

6836
def __str__(self):
69-
return self.key
37+
return self.text
38+
39+
@property
40+
def text(self):
41+
return self.trans('text')
42+
43+
@property
44+
def help(self):
45+
return self.trans('help')
7046

7147

72-
def create_profile_for_user(sender, **kwargs):
73-
user = kwargs['instance']
74-
if kwargs['created'] and not kwargs.get('raw', False):
75-
profile = Profile()
76-
profile.user = user
77-
profile.save()
48+
@python_2_unicode_compatible
49+
class AdditionalFieldValue(models.Model):
7850

79-
post_save.connect(create_profile_for_user, sender=User)
51+
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='additional_values')
52+
field = models.ForeignKey(AdditionalField, on_delete=models.CASCADE, related_name='+')
53+
value = models.CharField(max_length=256)
54+
55+
class Meta:
56+
ordering = ('user', 'field')
57+
58+
verbose_name = _('Additional field value')
59+
verbose_name_plural = _('Additional field values')
60+
61+
def __str__(self):
62+
return self.user.username + '/' + self.field.key
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{% extends 'core/page.html' %}
2+
{% load i18n %}
3+
4+
{% block page %}
5+
6+
<h1>{% trans "E-mail Addresses" %}</h1>
7+
8+
{% if user.emailaddress_set.all %}
9+
10+
<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p>
11+
12+
<form class="email-form" action="{% url 'account_email' %}" class="email_list" method="post">
13+
{% csrf_token %}
14+
15+
<fieldset>
16+
17+
{% for emailaddress in user.emailaddress_set.all %}
18+
19+
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
20+
<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}}"/>
21+
22+
{{ emailaddress.email }}
23+
24+
<div class="pull-right">
25+
{% if emailaddress.primary %}
26+
<span class="label label-primary">{% trans "Primary" %}</span>
27+
{% endif %}
28+
29+
{% if emailaddress.verified %}
30+
<span class="label label-success">{% trans "Verified" %}</span>
31+
{% else %}
32+
<span class="label label-warning">{% trans "Unverified" %}</span>
33+
{% endif %}
34+
</div>
35+
</label>
36+
37+
{% endfor %}
38+
39+
<div class="email-form-buttons">
40+
<button class="btn btn-default" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
41+
<button class="btn btn-default" type="submit" name="action_send" >{% trans 'Re-send Verification' %}</button>
42+
<button class="btn btn-primary" type="submit" name="action_remove" >{% trans 'Remove' %}</button>
43+
</div>
44+
45+
</fieldset>
46+
</form>
47+
48+
{% else %}
49+
50+
<p>
51+
<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." %}
52+
</p>
53+
54+
{% endif %}
55+
56+
<h2>{% trans "Add E-mail Address" %}</h2>
57+
58+
<form method="post" action="{% url 'account_email' %}" class="add_email">
59+
{% csrf_token %}
60+
61+
{% include 'core/bootstrap_form_fields.html' %}
62+
63+
<button class="btn btn-default" name="action_add" type="submit">{% trans "Add E-mail" %}</button>
64+
</form>
65+
66+
{% endblock %}
67+
68+
{% block extra_body %}
69+
<script type="text/javascript">
70+
(function() {
71+
var message = "{% trans 'Do you really want to remove the selected e-mail address?' %}";
72+
var actions = document.getElementsByName('action_remove');
73+
if (actions.length) {
74+
actions[0].addEventListener("click", function(e) {
75+
if (! confirm(message)) {
76+
e.preventDefault();
77+
}
78+
});
79+
}
80+
})();
81+
</script>
82+
{% endblock %}

0 commit comments

Comments
 (0)