accounts profile
This commit is contained in:
parent
ebec3575c4
commit
6610f86801
1
accounts/__init__.py
Normal file
1
accounts/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
default_app_config = 'accounts.apps.AccountsConfig'
|
8
accounts/apps.py
Normal file
8
accounts/apps.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
name = 'accounts'
|
||||
|
||||
def ready(self):
|
||||
import accounts.signals # noqa
|
20
accounts/forms.py
Normal file
20
accounts/forms.py
Normal file
@ -0,0 +1,20 @@
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.forms.models import modelform_factory
|
||||
|
||||
|
||||
from .models import User, Profile
|
||||
|
||||
|
||||
# email MUST be validated, we do not allow to edit it
|
||||
UserForm = modelform_factory(User, fields=['first_name', 'last_name', 'username'])
|
||||
|
||||
ProfileForm = modelform_factory(Profile, fields=[
|
||||
'phone_number', 'biography', 'twitter', 'website',
|
||||
'linkedin', 'facebook', 'mastodon'])
|
||||
|
||||
|
||||
class EmailAuthenticationForm(AuthenticationForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['username'].label = _('Email address')
|
48
accounts/migrations/0001_initial.py
Normal file
48
accounts/migrations/0001_initial.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.1 on 2017-11-18 20:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def profile_forward(apps, schema_editor):
|
||||
User = apps.get_model(settings.AUTH_USER_MODEL)
|
||||
Profile = apps.get_model("accounts", "Profile")
|
||||
db_alias = schema_editor.connection.alias
|
||||
for user in User.objects.using(db_alias).all():
|
||||
Profile.objects.using(db_alias).get_or_create(user=user)
|
||||
|
||||
|
||||
def profile_backward(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
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')),
|
||||
('phone_number', models.CharField(blank=True, default='', max_length=16, verbose_name='Phone number')),
|
||||
('sms_prefered', models.BooleanField(default=False, verbose_name='SMS prefered')),
|
||||
('biography', models.TextField(blank=True, verbose_name='Biography')),
|
||||
('twitter', models.CharField(blank=True, default='', max_length=100, verbose_name='Twitter')),
|
||||
('linkedin', models.CharField(blank=True, default='', max_length=100, verbose_name='LinkedIn')),
|
||||
('github', models.CharField(blank=True, default='', max_length=100, verbose_name='Github')),
|
||||
('website', models.CharField(blank=True, default='', max_length=100, verbose_name='Website')),
|
||||
('facebook', models.CharField(blank=True, default='', max_length=100, verbose_name='Facebook')),
|
||||
('mastodon', models.CharField(blank=True, default='', max_length=100, verbose_name='Mastodon')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.RunPython(profile_forward, profile_backward),
|
||||
]
|
0
accounts/migrations/__init__.py
Normal file
0
accounts/migrations/__init__.py
Normal file
25
accounts/models.py
Normal file
25
accounts/models.py
Normal file
@ -0,0 +1,25 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class Profile(models.Model):
|
||||
|
||||
user = models.OneToOneField(User)
|
||||
phone_number = models.CharField(max_length=16, blank=True, default='', verbose_name=_('Phone number'))
|
||||
sms_prefered = models.BooleanField(default=False, verbose_name=_('SMS prefered'))
|
||||
biography = models.TextField(blank=True, verbose_name=_('Biography'))
|
||||
|
||||
twitter = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Twitter'))
|
||||
linkedin = models.CharField(max_length=100, blank=True, default='', verbose_name=_('LinkedIn'))
|
||||
github = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Github'))
|
||||
website = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Website'))
|
||||
facebook = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Facebook'))
|
||||
mastodon = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Mastodon'))
|
||||
|
||||
def __str__(self):
|
||||
return self.user.get_full_name() or self.user.username
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('profile')
|
35
accounts/signals.py
Normal file
35
accounts/signals.py
Normal file
@ -0,0 +1,35 @@
|
||||
from django.contrib import messages
|
||||
#from django.contrib.auth.models import User
|
||||
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
||||
#from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
#from django.utils.translation import ugettext_noop
|
||||
|
||||
from ponyconf.decorators import disable_for_loaddata
|
||||
|
||||
from .models import User, Profile
|
||||
|
||||
|
||||
@receiver(user_logged_in)
|
||||
def on_user_logged_in(sender, request, user, **kwargs):
|
||||
#participation, created = Participation.objects.get_or_create(user=user, site=get_current_site(request))
|
||||
#if user.is_superuser:
|
||||
# participation.orga = True
|
||||
# participation.save()
|
||||
#if created:
|
||||
# messages.info(request, "Please check your profile!\n", fail_silently=True) # FIXME
|
||||
messages.success(request, _('Welcome!'), fail_silently=True) # FIXME
|
||||
|
||||
|
||||
@receiver(user_logged_out)
|
||||
def on_user_logged_out(sender, request, **kwargs):
|
||||
messages.success(request, _('Goodbye!'), fail_silently=True) # FIXME
|
||||
|
||||
|
||||
@receiver(post_save, sender=User, weak=False, dispatch_uid='create_profile')
|
||||
@disable_for_loaddata
|
||||
def create_profile(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
Profile.objects.create(user=instance)
|
41
accounts/templates/accounts/profile.html
Normal file
41
accounts/templates/accounts/profile.html
Normal file
@ -0,0 +1,41 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load bootstrap3 i18n %}
|
||||
|
||||
{% block profiletab %} class="active"{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3>{% trans "Profile" %}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
{% for form in forms %}
|
||||
{% bootstrap_form form layout="horizontal" %}
|
||||
{% endfor %}
|
||||
{% buttons layout="horizontal" %}
|
||||
<button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
|
||||
{% for url, class, text in buttons %}
|
||||
<a href="{% url url %}" class="btn btn-{{ class }}">{{ text }}</a>
|
||||
{% endfor %}
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{% url 'home' %}{% endif %}" class="btn btn-default">{% trans "Cancel" %}</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
{% for form in forms %}{{ form.media.css }}{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js_end %}
|
||||
{{ block.super }}
|
||||
{% for form in forms %}{{ form.media.js }}{% endfor %}
|
||||
{% endblock %}
|
15
accounts/urls.py
Normal file
15
accounts/urls.py
Normal file
@ -0,0 +1,15 @@
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^profile/$', views.profile, name='profile'),
|
||||
url(r'accounts/login/', views.EmailLoginView.as_view(), {'extra_context': {'buttons': [views.RESET_PASSWORD_BUTTON]}}, name='login'),
|
||||
#url(r'^login/$', auth_views.login, {'extra_context': {'buttons': [views.RESET_PASSWORD_BUTTON]}}, name='login'),
|
||||
url(r'^logout/$', auth_views.logout, {'next_page': settings.LOGOUT_REDIRECT_URL}, name='logout'),
|
||||
#url(r'^avatar/', include('avatar.urls')),
|
||||
url(r'', include('django.contrib.auth.urls')),
|
||||
#url(r'', include('registration.backends.default.urls')),
|
||||
]
|
34
accounts/views.py
Normal file
34
accounts/views.py
Normal file
@ -0,0 +1,34 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.shortcuts import redirect, render
|
||||
from django.contrib import messages
|
||||
|
||||
from accounts.models import User, Profile
|
||||
from accounts.forms import UserForm, ProfileForm, EmailAuthenticationForm
|
||||
|
||||
|
||||
RESET_PASSWORD_BUTTON = ('password_reset', 'warning', _('Reset your password'))
|
||||
CHANGE_PASSWORD_BUTTON = ('password_change', 'warning', _('Change password'))
|
||||
|
||||
|
||||
class EmailLoginView(LoginView):
|
||||
authentication_form = EmailAuthenticationForm
|
||||
|
||||
|
||||
@login_required
|
||||
def profile(request):
|
||||
user_form = UserForm(request.POST or None, instance=request.user)
|
||||
profile_form = ProfileForm(request.POST or None, instance=request.user.profile)
|
||||
forms = [user_form, profile_form]
|
||||
if request.method == 'POST':
|
||||
if all(map(lambda form: form.is_valid(), forms)):
|
||||
for form in forms:
|
||||
form.save()
|
||||
messages.success(request, _('Profile updated successfully.'))
|
||||
else:
|
||||
messages.error(request, _('Please correct those errors.'))
|
||||
return render(request, 'accounts/profile.html', {
|
||||
'forms': forms,
|
||||
'buttons': [CHANGE_PASSWORD_BUTTON],
|
||||
})
|
@ -27,10 +27,10 @@ urlpatterns = [
|
||||
url(r'^cfp/(?P<talk_id>[\w\-]+)/(?P<participant_id>[\w\-]+)/confirm/$', views.talk_acknowledgment, {'confirm': True}, name='talk-confirm'),
|
||||
url(r'^cfp/(?P<talk_id>[\w\-]+)/(?P<participant_id>[\w\-]+)/desist/$', views.talk_acknowledgment, {'confirm': False}, name='talk-desist'),
|
||||
# End backward compatibility
|
||||
url(r'^volunteer/$', views.volunteer_enrole, name='volunteer-enrole'),
|
||||
url(r'^volunteer/(?P<volunteer_token>[\w\-]+)/$', views.volunteer_home, name='volunteer-home'),
|
||||
url(r'^volunteer/(?P<volunteer_token>[\w\-]+)/join/(?P<activity>[\w\-]+)/$', views.volunteer_update_activity, {'join': True}, name='volunteer-join'),
|
||||
url(r'^volunteer/(?P<volunteer_token>[\w\-]+)/quit/(?P<activity>[\w\-]+)/$', views.volunteer_update_activity, {'join': False}, name='volunteer-quit'),
|
||||
url(r'^volunteer/enrole/$', views.volunteer_enrole, name='volunteer-enrole'),
|
||||
url(r'^volunteer/(?:(?P<volunteer_token>[\w\-]+)/)?$', views.volunteer_home, name='volunteer-home'),
|
||||
url(r'^volunteer/(?:(?P<volunteer_token>[\w\-]+)/)?join/(?P<activity>[\w\-]+)/$', views.volunteer_update_activity, {'join': True}, name='volunteer-join'),
|
||||
url(r'^volunteer/(?:(?P<volunteer_token>[\w\-]+)/)?quit/(?P<activity>[\w\-]+)/$', views.volunteer_update_activity, {'join': False}, name='volunteer-quit'),
|
||||
#url(r'^talk/(?P<talk_id>[\w\-]+)/$', views.talk_show, name='show-talk'),
|
||||
#url(r'^speaker/(?P<participant_id>[\w\-]+)/$', views.speaker_show, name='show-speaker'),
|
||||
url(r'^staff/$', views.staff, name='staff'),
|
||||
|
@ -49,9 +49,10 @@ def volunteer_enrole(request):
|
||||
if Volunteer.objects.filter(site=request.conference.site, email=request.user.email).exists():
|
||||
return redirect(reverse('volunteer-home'))
|
||||
elif not request.POST:
|
||||
# TODO: import biography, phone number and sms_prefered from User profile
|
||||
initial.update({
|
||||
'name': request.user.get_full_name(),
|
||||
'phone_number': request.user.profile.phone_number,
|
||||
'sms_prefered': request.user.profile.sms_prefered,
|
||||
})
|
||||
form = VolunteerForm(request.POST or None, initial=initial, conference=request.conference)
|
||||
if request.user.is_authenticated():
|
||||
@ -174,9 +175,9 @@ def proposal_home(request):
|
||||
if Participant.objects.filter(site=request.conference.site, email=request.user.email).exists():
|
||||
return redirect(reverse('proposal-dashboard'))
|
||||
elif not request.POST:
|
||||
# TODO: import biography from User profile
|
||||
initial.update({
|
||||
'name': request.user.get_full_name(),
|
||||
'biography': request.user.profile.biography,
|
||||
})
|
||||
fields.remove('email')
|
||||
NewSpeakerForm = modelform_factory(Participant, form=ParticipantForm, fields=fields)
|
||||
|
@ -37,8 +37,8 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sites',
|
||||
|
||||
# our apps
|
||||
#'accounts',
|
||||
'ponyconf',
|
||||
'accounts',
|
||||
'cfp',
|
||||
'mailing',
|
||||
#'planning',
|
||||
|
@ -43,12 +43,7 @@
|
||||
<li{% block stafftab %}{% endblock %}><a href="{% url 'staff' %}"><span class="glyphicon glyphicon-blackboard"></span> {% trans "Organisation" %}</a></li>
|
||||
<li{% block admintab %}{% endblock %}><a href="{% url 'admin' %}"><span class="glyphicon glyphicon-cog"></span> {% trans "Administration" %}</a></li>
|
||||
{% endif %}
|
||||
{% comment %}
|
||||
<li{% block talkstab %}{% endblock %}><a href="{% url 'talk-list' %}"><span class="glyphicon glyphicon-blackboard"></span> {% trans "Talks" %}</a></li>
|
||||
<li{% block speakerstab %}{% endblock %}><a href="{% url 'participant-list' %}"><span class="glyphicon glyphicon-bullhorn"></span> {% trans "Speakers" %}</a></li>
|
||||
<li{% block inboxtab %}{% endblock %}><a href="{% url 'inbox' %}"><span class="glyphicon glyphicon-envelope"></span> Inbox</a></li>
|
||||
<li{% block profiletab %}{% endblock %}><a href="{% url 'profile' %}"><span class="glyphicon glyphicon-user"></span> {% trans "Profile" %}</a></li>
|
||||
{% endcomment %}
|
||||
<li><a href="{% url 'logout' %}"><span class="glyphicon glyphicon-log-out"></span> {% trans "Logout" %}</a></li>
|
||||
{% else %}
|
||||
<li{% block logintab %}{% endblock %}><a href="{% url 'staff' %}"><span class="glyphicon glyphicon-log-in"></span> {% trans "Staff" %}</a></li>
|
||||
|
@ -15,26 +15,12 @@ Including another URLconf
|
||||
"""
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class EmailAuthenticationForm(AuthenticationForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['username'].label = _('Email address')
|
||||
|
||||
|
||||
class EmailLoginView(LoginView):
|
||||
authentication_form = EmailAuthenticationForm
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/django/', admin.site.urls),
|
||||
url(r'accounts/login/', EmailLoginView.as_view()),
|
||||
url(r'accounts/', include('django.contrib.auth.urls')),
|
||||
url(r'^accounts/', include('accounts.urls')),
|
||||
url(r'^', include('cfp.urls')),
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user