-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21 from rdmorganiser/accounts
Accounts
- Loading branch information
Showing
84 changed files
with
1,756 additions
and
1,028 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 %} |
Oops, something went wrong.