Finish profile of users

This commit is contained in:
Skia 2016-08-13 05:33:09 +02:00
parent 5113d8fda5
commit c0a66f9a38
9 changed files with 208 additions and 31 deletions

View File

@ -32,7 +32,7 @@ class Command(BaseCommand):
for g in settings.SITH_GROUPS.values(): for g in settings.SITH_GROUPS.values():
Group(id=g['id'], name=g['name']).save() Group(id=g['id'], name=g['name']).save()
self.reset_index("core", "auth") self.reset_index("core", "auth")
root = User(username='root', last_name="", first_name="Bibou", root = User(id=0, username='root', last_name="", first_name="Bibou",
email="ae.info@utbm.fr", email="ae.info@utbm.fr",
date_of_birth="1942-06-12", date_of_birth="1942-06-12",
is_superuser=True, is_staff=True) is_superuser=True, is_staff=True)
@ -76,6 +76,19 @@ class Command(BaseCommand):
p.save() p.save()
PageRev(page=p, title="Wiki index", author=root, content=""" PageRev(page=p, title="Wiki index", author=root, content="""
Welcome to the wiki page! Welcome to the wiki page!
""").save()
p = Page(name="services")
p.set_lock(root)
p.save()
p.view_groups=[settings.SITH_GROUPS['public']['id']]
p.set_lock(root)
PageRev(page=p, title="Services", author=root, content="""
| | | |
| :---: | :---: | :---: | :---: |
| [Eboutic](/eboutic) | [Laverie](/launderette) | Matmat | [Fichiers](/file) |
| SAS | Weekmail | Forum | |
""").save() """).save()
p = Page(name="launderette") p = Page(name="launderette")

View File

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import phonenumber_field.modelfields
from django.utils.timezone import utc
import datetime
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20160811_0450'),
]
operations = [
migrations.AddField(
model_name='user',
name='address',
field=models.CharField(default='', verbose_name='address', max_length=128, blank=True),
),
migrations.AddField(
model_name='user',
name='last_update',
field=models.DateField(default=datetime.datetime(2016, 8, 13, 3, 22, 21, 699918, tzinfo=utc), verbose_name='last update', auto_now=True),
preserve_default=False,
),
migrations.AddField(
model_name='user',
name='parent_address',
field=models.CharField(default='', verbose_name='parent address', max_length=128, blank=True),
),
migrations.AddField(
model_name='user',
name='parent_phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, verbose_name='parent phone', max_length=128, null=True),
),
migrations.AddField(
model_name='user',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, verbose_name='phone', max_length=128, null=True),
),
migrations.AddField(
model_name='user',
name='second_email',
field=models.EmailField(blank=True, verbose_name='second email address', max_length=254, null=True),
),
migrations.AlterField(
model_name='user',
name='avatar_pict',
field=models.OneToOneField(verbose_name='avatar', on_delete=django.db.models.deletion.SET_NULL, related_name='avatar_of', blank=True, to='core.SithFile', null=True),
),
migrations.AlterField(
model_name='user',
name='department',
field=models.CharField(default='NA', verbose_name='department', max_length=15, choices=[('TC', 'TC'), ('IMSI', 'IMSI'), ('IMAP', 'IMAP'), ('INFO', 'INFO'), ('GI', 'GI'), ('E', 'E'), ('EE', 'EE'), ('GESC', 'GESC'), ('GMC', 'GMC'), ('MC', 'MC'), ('EDIM', 'EDIM'), ('HUMA', 'Humanities'), ('NA', 'N/A')], blank=True),
),
migrations.AlterField(
model_name='user',
name='dpt_option',
field=models.CharField(default='', verbose_name='dpt option', max_length=32, blank=True),
),
migrations.AlterField(
model_name='user',
name='first_name',
field=models.CharField(verbose_name='first name', max_length=64),
),
migrations.AlterField(
model_name='user',
name='forum_signature',
field=models.TextField(default='', verbose_name='forum signature', max_length=256, blank=True),
),
migrations.AlterField(
model_name='user',
name='last_name',
field=models.CharField(verbose_name='last name', max_length=64),
),
migrations.AlterField(
model_name='user',
name='nick_name',
field=models.CharField(blank=True, verbose_name='nick name', max_length=64, null=True),
),
migrations.AlterField(
model_name='user',
name='profile_pict',
field=models.OneToOneField(verbose_name='profile', on_delete=django.db.models.deletion.SET_NULL, related_name='profile_of', blank=True, to='core.SithFile', null=True),
),
migrations.AlterField(
model_name='user',
name='quote',
field=models.CharField(default='', verbose_name='quote', max_length=256, blank=True),
),
migrations.AlterField(
model_name='user',
name='role',
field=models.CharField(default='', verbose_name='role', max_length=15, choices=[('STUDENT', 'Student'), ('ADMINISTRATIVE', 'Administrative agent'), ('TEACHER', 'Teacher'), ('AGENT', 'Agent'), ('DOCTOR', 'Doctor'), ('FORMER STUDENT', 'Former student'), ('SERVICE', 'Service')], blank=True),
),
migrations.AlterField(
model_name='user',
name='school',
field=models.CharField(default='', verbose_name='school', max_length=80, blank=True),
),
migrations.AlterField(
model_name='user',
name='scrub_pict',
field=models.OneToOneField(verbose_name='scrub', on_delete=django.db.models.deletion.SET_NULL, related_name='scrub_of', blank=True, to='core.SithFile', null=True),
),
migrations.AlterField(
model_name='user',
name='semester',
field=models.CharField(default='', verbose_name='semester', max_length=5, blank=True),
),
migrations.AlterField(
model_name='user',
name='tshirt_size',
field=models.CharField(default='-', verbose_name='tshirt size', max_length=5, choices=[('-', '-'), ('XS', 'XS'), ('S', 'S'), ('M', 'M'), ('L', 'L'), ('XL', 'XL'), ('XXL', 'XXL'), ('XXXL', 'XXXL')]),
),
]

View File

@ -7,6 +7,8 @@ from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from phonenumber_field.modelfields import PhoneNumberField
from datetime import datetime, timedelta, date from datetime import datetime, timedelta, date
import unicodedata import unicodedata
@ -82,11 +84,11 @@ class User(AbstractBaseUser):
'unique': _("A user with that username already exists."), 'unique': _("A user with that username already exists."),
}, },
) )
first_name = models.CharField(_('first name'), max_length=30) first_name = models.CharField(_('first name'), max_length=64)
last_name = models.CharField(_('last name'), max_length=30) last_name = models.CharField(_('last name'), max_length=64)
email = models.EmailField(_('email address'), unique=True) email = models.EmailField(_('email address'), unique=True)
date_of_birth = models.DateField(_('date of birth'), blank=True, null=True) date_of_birth = models.DateField(_('date of birth'), blank=True, null=True)
nick_name = models.CharField(_('nick name'), max_length=30, blank=True) nick_name = models.CharField(_('nick name'), max_length=64, null=True, blank=True)
is_staff = models.BooleanField( is_staff = models.BooleanField(
_('staff status'), _('staff status'),
default=False, default=False,
@ -101,6 +103,7 @@ class User(AbstractBaseUser):
), ),
) )
date_joined = models.DateField(_('date joined'), auto_now_add=True) date_joined = models.DateField(_('date joined'), auto_now_add=True)
last_update = models.DateField(_('last update'), auto_now=True)
is_superuser = models.BooleanField( is_superuser = models.BooleanField(
_('superuser'), _('superuser'),
default=False, default=False,
@ -110,9 +113,12 @@ class User(AbstractBaseUser):
) )
groups = models.ManyToManyField(RealGroup, related_name='users', blank=True) groups = models.ManyToManyField(RealGroup, related_name='users', blank=True)
home = models.OneToOneField('SithFile', related_name='home_of', verbose_name=_("home"), null=True, blank=True) home = models.OneToOneField('SithFile', related_name='home_of', verbose_name=_("home"), null=True, blank=True)
profile_pict = models.OneToOneField('SithFile', related_name='profile_of', verbose_name=_("profile"), null=True, blank=True) profile_pict = models.OneToOneField('SithFile', related_name='profile_of', verbose_name=_("profile"), null=True,
avatar_pict = models.OneToOneField('SithFile', related_name='avatar_of', verbose_name=_("avatar"), null=True, blank=True) blank=True, on_delete=models.SET_NULL)
scrub_pict = models.OneToOneField('SithFile', related_name='scrub_of', verbose_name=_("scrub"), null=True, blank=True) avatar_pict = models.OneToOneField('SithFile', related_name='avatar_of', verbose_name=_("avatar"), null=True,
blank=True, on_delete=models.SET_NULL)
scrub_pict = models.OneToOneField('SithFile', related_name='scrub_of', verbose_name=_("scrub"), null=True,
blank=True, on_delete=models.SET_NULL)
sex = models.CharField(_("sex"), max_length=10, choices=[("MAN", _("Man")), ("WOMAN", _("Woman"))], default="MAN") sex = models.CharField(_("sex"), max_length=10, choices=[("MAN", _("Man")), ("WOMAN", _("Woman"))], default="MAN")
tshirt_size = models.CharField(_("tshirt size"), max_length=5, choices=[ tshirt_size = models.CharField(_("tshirt size"), max_length=5, choices=[
("-", _("-")), ("-", _("-")),
@ -123,7 +129,7 @@ class User(AbstractBaseUser):
("XL", _("XL")), ("XL", _("XL")),
("XXL", _("XXL")), ("XXL", _("XXL")),
("XXXL", _("XXXL")), ("XXXL", _("XXXL")),
], default="M") ], default="-")
role = models.CharField(_("role"), max_length=15, choices=[ role = models.CharField(_("role"), max_length=15, choices=[
("STUDENT", _("Student")), ("STUDENT", _("Student")),
("ADMINISTRATIVE", _("Administrative agent")), ("ADMINISTRATIVE", _("Administrative agent")),
@ -132,7 +138,7 @@ class User(AbstractBaseUser):
("DOCTOR", _("Doctor")), ("DOCTOR", _("Doctor")),
("FORMER STUDENT", _("Former student")), ("FORMER STUDENT", _("Former student")),
("SERVICE", _("Service")), ("SERVICE", _("Service")),
], default="STUDENT") ], blank=True, default="")
department = models.CharField(_("department"), max_length=15, choices=[ department = models.CharField(_("department"), max_length=15, choices=[
("TC", _("TC")), ("TC", _("TC")),
("IMSI", _("IMSI")), ("IMSI", _("IMSI")),
@ -145,16 +151,20 @@ class User(AbstractBaseUser):
("GMC", _("GMC")), ("GMC", _("GMC")),
("MC", _("MC")), ("MC", _("MC")),
("EDIM", _("EDIM")), ("EDIM", _("EDIM")),
("HUMAN", _("Humanities")), ("HUMA", _("Humanities")),
("NA", _("N/A")), ("NA", _("N/A")),
], default="NA") ], default="NA", blank=True)
dpt_option = models.CharField(_("dpt option"), max_length=32, null=True, blank=True) dpt_option = models.CharField(_("dpt option"), max_length=32, blank=True, default="")
semester = models.CharField(_("semester"), max_length=5, null=True, blank=True) semester = models.CharField(_("semester"), max_length=5, blank=True, default="")
quote = models.CharField(_("quote"), max_length=64, null=True, blank=True) quote = models.CharField(_("quote"), max_length=256, blank=True, default="")
school = models.CharField(_("school"), max_length=32, null=True, blank=True) school = models.CharField(_("school"), max_length=80, blank=True, default="")
promo = models.IntegerField(_("promo"), validators=[validate_promo], null=True, blank=True) promo = models.IntegerField(_("promo"), validators=[validate_promo], null=True, blank=True)
forum_signature = models.TextField(_("forum signature"), max_length=256, null=True, blank=True) forum_signature = models.TextField(_("forum signature"), max_length=256, blank=True, default="")
# TODO: add phone numbers with https://github.com/stefanfoulis/django-phonenumber-field second_email = models.EmailField(_('second email address'), null=True, blank=True)
phone = PhoneNumberField(_("phone"), null=True, blank=True)
parent_phone = PhoneNumberField(_("parent phone"), null=True, blank=True)
address = models.CharField(_("address"), max_length=128, blank=True, default="")
parent_address = models.CharField(_("parent address"), max_length=128, blank=True, default="")
objects = UserManager() objects = UserManager()
@ -217,7 +227,7 @@ class User(AbstractBaseUser):
with transaction.atomic(): with transaction.atomic():
if self.id: if self.id:
old = User.objects.filter(id=self.id).first() old = User.objects.filter(id=self.id).first()
if old.username != self.username: if old and old.username != self.username:
self._change_username(self.username) self._change_username(self.username)
super(User, self).save(*args, **kwargs) super(User, self).save(*args, **kwargs)
@ -263,8 +273,8 @@ class User(AbstractBaseUser):
Returns the display name of the user. Returns the display name of the user.
A nickname if possible, otherwise, the full name A nickname if possible, otherwise, the full name
""" """
if self.nick_name != "": if self.nick_name:
return self.nick_name return "%s (%s)" % (self.get_full_name(), self.nick_name)
return self.get_full_name() return self.get_full_name()
def email_user(self, subject, message, from_email=None, **kwargs): def email_user(self, subject, message, from_email=None, **kwargs):
@ -463,7 +473,7 @@ class SithFile(models.Model):
if attr == "is_file": if attr == "is_file":
return not self.is_folder return not self.is_folder
else: else:
return object.__getattribute__(self, attr) return super(SithFile, self).__getattribute__(attr)
def __str__(self): def __str__(self):
if self.is_folder: if self.is_folder:

View File

@ -60,6 +60,7 @@ nav a:hover {
margin: 0px auto; margin: 0px auto;
padding: 1em 1%; padding: 1em 1%;
background: white; background: white;
overflow: auto;
} }
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {

View File

@ -37,7 +37,7 @@
<a href="{{ url('core:page', page_name="Index") }}">{% trans %}Wiki{% endtrans %}</a> <a href="{{ url('core:page', page_name="Index") }}">{% trans %}Wiki{% endtrans %}</a>
<a href="{{ url('core:page_list') }}">{% trans %}Pages{% endtrans %}</a> <a href="{{ url('core:page_list') }}">{% trans %}Pages{% endtrans %}</a>
<a href="{{ url('club:club_list') }}">{% trans %}Clubs{% endtrans %}</a> <a href="{{ url('club:club_list') }}">{% trans %}Clubs{% endtrans %}</a>
<a href="{{ url('core:page', "Services") }}">{% trans %}Services{% endtrans %}</a> <a href="{{ url('core:page', "services") }}">{% trans %}Services{% endtrans %}</a>
</nav> </nav>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -2,6 +2,7 @@
{% block content %} {% block content %}
<div class="tool-bar"> <div class="tool-bar">
<div>{{ profile.get_display_name() }}</div>
<div class="tools"> <div class="tools">
<a href="{{ url('core:user_profile', user_id=profile.id) }}">{% trans %}Infos{% endtrans %}</a> <a href="{{ url('core:user_profile', user_id=profile.id) }}">{% trans %}Infos{% endtrans %}</a>
{% if can_edit(profile, request.user) or user.id == profile.id %} {% if can_edit(profile, request.user) or user.id == profile.id %}
@ -16,7 +17,6 @@
<a href="{{ url('core:user_account', user_id=profile.id) }}">{% trans %}Account{% endtrans %}</a> <a href="{{ url('core:user_account', user_id=profile.id) }}">{% trans %}Account{% endtrans %}</a>
{% endif %} {% endif %}
</div> </div>
<h5>{{ profile.get_display_name() }}</h5>
</div> </div>
<hr> <hr>

View File

@ -1,5 +1,9 @@
{% extends "core/user_base.jinja" %} {% extends "core/user_base.jinja" %}
{% block title %}
{% trans %}Edit user{% endtrans %}
{% endblock %}
{% block infos %} {% block infos %}
<h2>{% trans %}Edit user profile{% endtrans %}</h2> <h2>{% trans %}Edit user profile{% endtrans %}</h2>
<form action="" method="post" enctype="multipart/form-data"> <form action="" method="post" enctype="multipart/form-data">

View File

@ -5,10 +5,13 @@ from django.core.exceptions import ValidationError
from django.contrib.auth import logout, login, authenticate from django.contrib.auth import logout, login, authenticate
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
import logging import logging
from core.models import User, Page, RealGroup, SithFile from core.models import User, Page, RealGroup, SithFile
# Widgets # Widgets
class SelectSingle(Select): class SelectSingle(Select):
@ -67,6 +70,26 @@ class RegisteringForm(UserCreationForm):
user.save() user.save()
return user return user
# Image utils
from io import BytesIO
from PIL import Image
def scale_dimension(width, height, long_edge):
if width > height:
ratio = long_edge * 1. / width
else:
ratio = long_edge * 1. / height
return int(width * ratio), int(height * ratio)
def resize_image(im, edge, format):
from django.core.files.base import ContentFile
(w, h) = im.size
(width, height) = scale_dimension(w, h, long_edge=edge)
content = BytesIO()
im.resize((width, height), Image.ANTIALIAS).save(fp=content, format=format, dpi=[72, 72])
return ContentFile(content.getvalue())
class UserProfileForm(forms.ModelForm): class UserProfileForm(forms.ModelForm):
""" """
Form handling the user profile, managing the files Form handling the user profile, managing the files
@ -76,13 +99,16 @@ class UserProfileForm(forms.ModelForm):
class Meta: class Meta:
model = User model = User
fields = ['first_name', 'last_name', 'nick_name', 'email', 'date_of_birth', 'profile_pict', 'avatar_pict', fields = ['first_name', 'last_name', 'nick_name', 'email', 'date_of_birth', 'profile_pict', 'avatar_pict',
'scrub_pict', 'sex', 'tshirt_size', 'role', 'department', 'dpt_option', 'semester', 'quote', 'school', 'scrub_pict', 'sex', 'second_email', 'address', 'parent_address', 'phone', 'parent_phone',
'promo', 'forum_signature'] 'tshirt_size', 'role', 'department', 'dpt_option', 'semester', 'quote', 'school', 'promo',
'forum_signature']
widgets = { widgets = {
'date_of_birth': SelectDate, 'date_of_birth': SelectDate,
'profile_pict': forms.ClearableFileInput, 'profile_pict': forms.ClearableFileInput,
'avatar_pict': forms.ClearableFileInput, 'avatar_pict': forms.ClearableFileInput,
'scrub_pict': forms.ClearableFileInput, 'scrub_pict': forms.ClearableFileInput,
'phone': PhoneNumberInternationalFallbackWidget,
'parent_phone': PhoneNumberInternationalFallbackWidget,
} }
labels = { labels = {
'profile_pict': _("Profile: you need to be visible on the picture, in order to be recognized (e.g. by the barmen)"), 'profile_pict': _("Profile: you need to be visible on the picture, in order to be recognized (e.g. by the barmen)"),
@ -111,13 +137,12 @@ class UserProfileForm(forms.ModelForm):
parent = SithFile.objects.filter(parent=None, name="profiles").first() parent = SithFile.objects.filter(parent=None, name="profiles").first()
for field,f in files: for field,f in files:
with transaction.atomic(): with transaction.atomic():
new_file = SithFile(parent=parent, name=self.generate_name(field, f), file=f, owner=self.instance, is_folder=False,
mime_type=f.content_type, size=f._size)
try: try:
if not (f.content_type == "image/jpeg" or im = Image.open(BytesIO(f.read()))
f.content_type == "image/png" or new_file = SithFile(parent=parent, name=self.generate_name(field, f),
f.content_type == "image/gif"): file=resize_image(im, 400, f.content_type.split('/')[-1]),
raise ValidationError(_("Bad image format, only jpeg, png, and gif are accepted")) owner=self.instance, is_folder=False, mime_type=f.content_type, size=f._size)
new_file.file.name = new_file.name
old = SithFile.objects.filter(parent=parent, name=new_file.name).first() old = SithFile.objects.filter(parent=parent, name=new_file.name).first()
if old: if old:
old.delete() old.delete()
@ -129,6 +154,10 @@ class UserProfileForm(forms.ModelForm):
self._errors.pop(field, None) self._errors.pop(field, None)
self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") % self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
{'file_name': f, 'msg': str(e.message)}) {'file_name': f, 'msg': str(e.message)})
except IOError:
self._errors.pop(field, None)
self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
{'file_name': f, 'msg': _("Bad image format, only jpeg, png, and gif are accepted")})
self._post_clean() self._post_clean()
class UserPropForm(forms.ModelForm): class UserPropForm(forms.ModelForm):

View File

@ -6,3 +6,4 @@ django-jinja
pyopenssl pyopenssl
pytz pytz
djangorestframework djangorestframework
django-phonenumber-field