Make file modale chooser and complete user profile
@ -38,6 +38,7 @@ class Command(BaseCommand):
|
||||
is_superuser=True, is_staff=True)
|
||||
root.set_password("plop")
|
||||
root.save()
|
||||
SithFile(parent=None, name="profiles", is_folder=True, owner=root).save()
|
||||
home_root = SithFile(parent=None, name="users", is_folder=True, owner=root)
|
||||
home_root.save()
|
||||
club_root = SithFile(parent=None, name="clubs", is_folder=True, owner=root)
|
||||
|
29
core/migrations/0003_auto_20160810_1949.py
Normal file
@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0002_user_home'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='avatar_pict',
|
||||
field=models.OneToOneField(related_name='avatar_of', verbose_name='avatar', to='core.SithFile', null=True, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='profile_pict',
|
||||
field=models.OneToOneField(related_name='profile_of', verbose_name='profile', to='core.SithFile', null=True, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='scrub_pict',
|
||||
field=models.OneToOneField(related_name='scrub_of', verbose_name='scrub', to='core.SithFile', null=True, blank=True),
|
||||
),
|
||||
]
|
19
core/migrations/0004_auto_20160811_0206.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0003_auto_20160810_1949'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='nick_name',
|
||||
field=models.CharField(verbose_name='nick name', max_length=30, blank=True),
|
||||
),
|
||||
]
|
65
core/migrations/0005_auto_20160811_0319.py
Normal file
@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import core.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0004_auto_20160811_0206'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='department',
|
||||
field=models.CharField(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'), ('HUMAN', 'Humanities'), ('NA', 'N/A')], default='NA', verbose_name='department'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='dpt_option',
|
||||
field=models.CharField(max_length=32, default='', verbose_name='dpt option'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='forum_signature',
|
||||
field=models.TextField(max_length=256, default='', verbose_name='forum signature'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='promo',
|
||||
field=models.IntegerField(blank=True, null=True, validators=[core.models.validate_promo], verbose_name='promo'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='quote',
|
||||
field=models.CharField(max_length=64, default='', verbose_name='quote'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='role',
|
||||
field=models.CharField(max_length=15, choices=[('STUDENT', 'Student'), ('ADMINISTRATIVE', 'Administrative agent'), ('TEACHER', 'Teacher'), ('AGENT', 'Agent'), ('DOCTOR', 'Doctor'), ('FORMER STUDENT', 'Former student'), ('SERVICE', 'Service')], default='STUDENT', verbose_name='role'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='school',
|
||||
field=models.CharField(max_length=32, default='', verbose_name='school'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='semester',
|
||||
field=models.CharField(max_length=5, default='', verbose_name='semester'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='sex',
|
||||
field=models.CharField(max_length=10, choices=[('MAN', 'Man'), ('WOMAN', 'Woman')], default='MAN', verbose_name='sex'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='tshirt_size',
|
||||
field=models.CharField(max_length=5, choices=[('-', '-'), ('XS', 'XS'), ('S', 'S'), ('M', 'M'), ('L', 'L'), ('XL', 'XL'), ('XXL', 'XXL'), ('XXXL', 'XXXL')], default='M', verbose_name='tshirt size'),
|
||||
),
|
||||
]
|
@ -7,7 +7,7 @@ from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, date
|
||||
|
||||
import unicodedata
|
||||
|
||||
@ -46,6 +46,15 @@ class RealGroup(Group):
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def validate_promo(value):
|
||||
start_year = settings.SITH_SCHOOL_START_YEAR
|
||||
delta = (date.today()+timedelta(days=180)).year - start_year
|
||||
if value < 0 or delta < value:
|
||||
raise ValidationError(
|
||||
_('%(value)s is not a valid promo (between 0 and %(end)s)'),
|
||||
params={'value': value, 'end': delta},
|
||||
)
|
||||
|
||||
class User(AbstractBaseUser):
|
||||
"""
|
||||
Defines the base user class, useable in every app
|
||||
@ -77,7 +86,7 @@ class User(AbstractBaseUser):
|
||||
last_name = models.CharField(_('last name'), max_length=30)
|
||||
email = models.EmailField(_('email address'), unique=True)
|
||||
date_of_birth = models.DateField(_('date of birth'), blank=True, null=True)
|
||||
nick_name = models.CharField(max_length=30, blank=True)
|
||||
nick_name = models.CharField(_('nick name'), max_length=30, blank=True)
|
||||
is_staff = models.BooleanField(
|
||||
_('staff status'),
|
||||
default=False,
|
||||
@ -101,6 +110,51 @@ class User(AbstractBaseUser):
|
||||
)
|
||||
groups = models.ManyToManyField(RealGroup, related_name='users', 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)
|
||||
avatar_pict = models.OneToOneField('SithFile', related_name='avatar_of', verbose_name=_("avatar"), null=True, blank=True)
|
||||
scrub_pict = models.OneToOneField('SithFile', related_name='scrub_of', verbose_name=_("scrub"), null=True, blank=True)
|
||||
sex = models.CharField(_("sex"), max_length=10, choices=[("MAN", _("Man")), ("WOMAN", _("Woman"))], default="MAN")
|
||||
tshirt_size = models.CharField(_("tshirt size"), max_length=5, choices=[
|
||||
("-", _("-")),
|
||||
("XS", _("XS")),
|
||||
("S", _("S")),
|
||||
("M", _("M")),
|
||||
("L", _("L")),
|
||||
("XL", _("XL")),
|
||||
("XXL", _("XXL")),
|
||||
("XXXL", _("XXXL")),
|
||||
], default="M")
|
||||
role = models.CharField(_("role"), max_length=15, choices=[
|
||||
("STUDENT", _("Student")),
|
||||
("ADMINISTRATIVE", _("Administrative agent")),
|
||||
("TEACHER", _("Teacher")),
|
||||
("AGENT", _("Agent")),
|
||||
("DOCTOR", _("Doctor")),
|
||||
("FORMER STUDENT", _("Former student")),
|
||||
("SERVICE", _("Service")),
|
||||
], default="STUDENT")
|
||||
department = models.CharField(_("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")),
|
||||
("HUMAN", _("Humanities")),
|
||||
("NA", _("N/A")),
|
||||
], default="NA")
|
||||
dpt_option = models.CharField(_("dpt option"), max_length=32, default="")
|
||||
semester = models.CharField(_("semester"), max_length=5, default="")
|
||||
quote = models.CharField(_("quote"), max_length=64, default="")
|
||||
school = models.CharField(_("school"), max_length=32, default="")
|
||||
promo = models.IntegerField(_("promo"), validators=[validate_promo], null=True, blank=True)
|
||||
forum_signature = models.TextField(_("forum signature"), max_length=256, default="")
|
||||
# TODO: add phone numbers with https://github.com/stefanfoulis/django-phonenumber-field
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
@ -341,6 +395,15 @@ class SithFile(models.Model):
|
||||
def is_owned_by(self, user):
|
||||
return user.id == self.owner.id
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
if hasattr(self, 'profile_of'):
|
||||
return user.can_view(self.profile_of)
|
||||
if hasattr(self, 'avatar_of'):
|
||||
return user.can_view(self.avatar_of)
|
||||
if hasattr(self, 'scrub_of'):
|
||||
return user.can_view(self.scrub_of)
|
||||
return False
|
||||
|
||||
def delete(self):
|
||||
for c in self.children.all():
|
||||
c.delete()
|
||||
@ -422,6 +485,9 @@ class SithFile(models.Model):
|
||||
def get_display_name(self):
|
||||
return self.name
|
||||
|
||||
def get_download_url(self):
|
||||
return reverse('core:download', kwargs={'file_id': self.id})
|
||||
|
||||
class LockError(Exception):
|
||||
"""There was a lock error on the object"""
|
||||
pass
|
||||
|
@ -2,6 +2,11 @@
|
||||
Super Form Reset
|
||||
----------------------------------------------------------------------------------------------------*/
|
||||
|
||||
form {
|
||||
margin: 0px auto;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
input,
|
||||
label,
|
||||
select,
|
||||
@ -22,6 +27,10 @@ textarea
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
label {
|
||||
min-width: 50%;
|
||||
}
|
||||
|
||||
/* Remove the stupid outer glow in Webkit */
|
||||
input:focus
|
||||
{
|
||||
@ -70,7 +79,8 @@ input[type=tel],
|
||||
input[type=text],
|
||||
input[type=time],
|
||||
input[type=url],
|
||||
input[type=week]
|
||||
input[type=week],
|
||||
textarea
|
||||
{
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
|
BIN
core/static/core/img/promo_01.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
core/static/core/img/promo_02.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
core/static/core/img/promo_03.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
core/static/core/img/promo_04.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
core/static/core/img/promo_05.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
core/static/core/img/promo_06.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
core/static/core/img/promo_07.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
core/static/core/img/promo_08.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
core/static/core/img/promo_09.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
core/static/core/img/promo_10.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
core/static/core/img/promo_11.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
core/static/core/img/promo_12.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
core/static/core/img/promo_13.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
core/static/core/img/promo_14.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
core/static/core/img/promo_15.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
core/static/core/img/promo_16.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
@ -1,11 +1,29 @@
|
||||
console.log('Guy');
|
||||
|
||||
$( function() {
|
||||
dialog = $( ".choose_file_widget" ).dialog({
|
||||
buttons = $(".choose_file_button");
|
||||
popups = $(".choose_file_widget");
|
||||
popups.dialog({
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
width: "80%",
|
||||
minHeight: "300",
|
||||
buttons: {
|
||||
"Choose": function() {
|
||||
console.log($("#file_id"));
|
||||
$("input[name="+$(this).attr('name')+"]").attr('value', $("#file_id").attr('value'));
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
});
|
||||
$('.select_date').datepicker({
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$( ".choose_file_button" ).button().on( "click", function() {
|
||||
dialog.dialog( "open" );
|
||||
popup = popups.filter("[name="+$(this).attr('name')+"]");
|
||||
console.log(popup);
|
||||
popup.html('<iframe src="/file/popup" width="95%"></iframe><span id="file_id" value="null" />');
|
||||
popup.dialog({title: $(this).attr('name')}).dialog( "open" );
|
||||
});
|
||||
} );
|
||||
|
@ -118,12 +118,16 @@ ul, ol {
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
font-size: 0.90em;
|
||||
}
|
||||
td {
|
||||
padding: 4px;
|
||||
border: solid 1px black;
|
||||
border-collapse: collapse;
|
||||
vertical-align: top;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 0;
|
||||
}
|
||||
td>ul {
|
||||
margin-top: 0px;
|
||||
@ -152,7 +156,27 @@ tbody>tr:hover {
|
||||
}
|
||||
|
||||
/*-----------------------------USER PROFILE----------------------------*/
|
||||
.user_profile {
|
||||
#user_profile {
|
||||
width: 80%;
|
||||
margin: 0px auto;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
#user_profile h4 { border-bottom: 1px solid grey; max-width: 60%; }
|
||||
#user_profile #pictures {
|
||||
width: 30%;
|
||||
float: right;
|
||||
font-style: italic;
|
||||
}
|
||||
#user_profile #nickname {
|
||||
font-style: italic;
|
||||
}
|
||||
#user_profile #pictures img {
|
||||
max-width: 96%;
|
||||
max-height: 96%;
|
||||
}
|
||||
#user_profile .promo_pict {
|
||||
height: 45px;
|
||||
}
|
||||
/*---------------------------------PAGE--------------------------------*/
|
||||
.page_content {
|
||||
|
@ -60,23 +60,25 @@
|
||||
{{ tests }}
|
||||
{% endblock %}
|
||||
-->
|
||||
{% block script %}
|
||||
<script src="{{ static('core/js/jquery-3.1.0.min.js') }}"></script>
|
||||
<script src="{{ static('core/js/ui/jquery-ui.min.js') }}"></script>
|
||||
<script src="{{ static('core/js/multiple-select.js') }}"></script>
|
||||
<script src="{{ static('core/js/script.js') }}"></script>
|
||||
<script>
|
||||
$('select:not([multiple])').multipleSelect({
|
||||
single: true,
|
||||
{% if not popup %}
|
||||
position: 'top',
|
||||
{% endif %}
|
||||
});
|
||||
$('select[multiple=multiple]').multipleSelect({
|
||||
filter: true,
|
||||
{% if not popup %}
|
||||
position: 'top',
|
||||
{% endif %}
|
||||
});
|
||||
$('.select_single').multipleSelect({
|
||||
single: true,
|
||||
{% if not popup %}
|
||||
position: 'top',
|
||||
{% endif %}
|
||||
});
|
||||
$('.select_multiple').multipleSelect({
|
||||
filter: true,
|
||||
{% if not popup %}
|
||||
position: 'top',
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,5 +1,9 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block title %}
|
||||
{% trans obj=object %}Edit {{ obj }}{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans obj=object %}Edit {{ obj }}{% endtrans %}</h2>
|
||||
<form action="" method="post">
|
||||
|
@ -42,8 +42,18 @@
|
||||
{% if not file.home_of and not file.home_of_club and file.parent %}
|
||||
<p><a href="{{ url('core:file_delete', file_id=file.id, popup=popup) }}">{% trans %}Delete{% endtrans %}</a></p>
|
||||
{% endif %}
|
||||
{% if popup %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
{% if popup and file.is_file %}
|
||||
<script>
|
||||
parent.$("#file_id").replaceWith('<span id="file_id" value="{{ file.id }}">{{ file.name }}</span>');
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -21,7 +21,9 @@
|
||||
|
||||
<form method="post" action="{{ url('core:login') }}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p() }}
|
||||
<p>{{ form.username.errors }}<label for="{{ form.username.name }}">{{ form.username.label }}
|
||||
</label><input id="id_username" maxlength="254" name="username" type="text" autofocus="autofocus" /></p>
|
||||
<p>{{ form.password.errors }}<label for="{{ form.password.name }}">{{ form.password.label }}</label>{{ form.password }}</p>
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<p><input type="submit" value="{% trans %}login{% endtrans %}"></p>
|
||||
</form>
|
||||
|
@ -5,12 +5,26 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block infos %}
|
||||
<h3>{% trans %}User Profile{% endtrans %}</h3>
|
||||
|
||||
<div class="user_profile">
|
||||
<div id="user_profile">
|
||||
<div id="pictures">
|
||||
{% if profile.profile_pict %}
|
||||
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
|
||||
{% endif %}
|
||||
<p><em>{{ profile.quote }}</em></p>
|
||||
</div>
|
||||
<h4>{{ profile.get_full_name() }}</h4>
|
||||
<p>{{ profile.nick_name }}</p>
|
||||
<p id="nickname">« {{ profile.nick_name }} »</p>
|
||||
<p>{% trans %}Born: {% endtrans %}{{ profile.date_of_birth|date("d/m/Y") }}</p>
|
||||
<p>{{ profile.department }}{{ profile.semester }}
|
||||
{% if profile.dpt_option %}
|
||||
<br>{% trans %}Option: {% endtrans %}{{ profile.dpt_option }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if profile.promo %}
|
||||
<p><img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" class="promo_pict" />
|
||||
{% trans %}Promo: {% endtrans %}{{ profile.promo }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user.membership.filter(end_date=None).exists() or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) %}
|
||||
|
@ -1,15 +1,31 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
{% extends "core/user_base.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
{% block infos %}
|
||||
<h2>{% trans %}Edit user profile{% endtrans %}</h2>
|
||||
<form action="" method="post">
|
||||
<form action="" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p() }}
|
||||
{% for field in form %}
|
||||
<p>{{ field.errors }}<label for="{{ field.name }}">{{ field.label }}
|
||||
{%- if field.name == "profile_pict" and form.instance.profile_pict -%}
|
||||
<br>{% trans %}Current profile: {% endtrans %}
|
||||
<img src="{{ form.instance.profile_pict.get_download_url() }}" title="{% trans %}Profile{% endtrans %}" width="50px" /><br>
|
||||
{%- elif field.name == "avatar_pict" and form.instance.avatar_pict -%}
|
||||
<br>{% trans %}Current avatar: {% endtrans %}
|
||||
<img src="{{ form.instance.avatar_pict.get_download_url() }}" title="{% trans %}Avatar{% endtrans %}" width="50px" /><br>
|
||||
{%- elif field.name == "scrub_pict" and form.instance.scrub_pict -%}
|
||||
<br>{% trans %}Current scrub: {% endtrans %}
|
||||
<img src="{{ form.instance.scrub_pict.get_download_url() }}" title="{% trans %}Scrub{% endtrans %}" width="50px" /><br>
|
||||
{%- endif %}</label> {{ field }}</p>
|
||||
{% endfor %}
|
||||
<p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p>
|
||||
<p>{% trans %}Username: {% endtrans %}{{ form.instance.username }}</p>
|
||||
{% if form.instance.customer %}
|
||||
<p>{% trans %}Account number: {% endtrans %}{{ form.instance.customer.account_id }}</p>
|
||||
{% endif %}
|
||||
{% if form.instance == user %}
|
||||
<p><a href="{{ url('core:password_change') }}">{% trans %}Change my password{% endtrans %}</a></p>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% if form.instance == user %}
|
||||
<p><a href="{{ url('core:password_change') }}">{% trans %}Change my password{% endtrans %}</a></p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
{% extends "core/user_base.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
{% block infos %}
|
||||
<h2>{% trans user_name=profile.get_full_name() %}Edit user groups for {{ user_name }}{% endtrans %}</h2>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
|
@ -8,8 +8,11 @@ from django.contrib.auth.forms import AuthenticationForm
|
||||
from core.models import Group
|
||||
|
||||
def forbidden(request):
|
||||
return HttpResponseForbidden(render(request, "core/403.jinja", context={'next': request.path, 'form':
|
||||
AuthenticationForm(), 'popup': request.resolver_match.kwargs['popup'] or ""}))
|
||||
try:
|
||||
return HttpResponseForbidden(render(request, "core/403.jinja", context={'next': request.path, 'form':
|
||||
AuthenticationForm(), 'popup': request.resolver_match.kwargs['popup'] or ""}))
|
||||
except:
|
||||
return HttpResponseForbidden(render(request, "core/403.jinja", context={'next': request.path, 'form': AuthenticationForm()}))
|
||||
|
||||
def not_found(request):
|
||||
return HttpResponseNotFound(render(request, "core/404.jinja"))
|
||||
|
@ -37,7 +37,7 @@ def send_file(request, file_id):
|
||||
response['Content-Disposition'] = 'inline; filename="%s"' % f.name
|
||||
return response
|
||||
|
||||
class AddFileForm(forms.Form):
|
||||
class AddFilesForm(forms.Form):
|
||||
folder_name = forms.CharField(label=_("Add a new folder"), max_length=30, required=False)
|
||||
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Files"),
|
||||
required=False)
|
||||
@ -128,7 +128,7 @@ class FileView(CanViewMixin, DetailView, FormMixin):
|
||||
pk_url_kwarg = "file_id"
|
||||
template_name = 'core/file_detail.jinja'
|
||||
context_object_name = "file"
|
||||
form_class = AddFileForm
|
||||
form_class = AddFilesForm
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.form = self.get_form()
|
||||
|
@ -1,11 +1,56 @@
|
||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, UserChangeForm
|
||||
from django import forms
|
||||
from django.db import transaction
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.contrib.auth import logout, login, authenticate
|
||||
from django.forms import CheckboxSelectMultiple, Select
|
||||
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput
|
||||
from django.utils.translation import ugettext as _
|
||||
import logging
|
||||
|
||||
from core.models import User, Page, RealGroup
|
||||
from core.models import User, Page, RealGroup, SithFile
|
||||
|
||||
# Widgets
|
||||
|
||||
class SelectSingle(Select):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
attrs['class'] = "select_single"
|
||||
else:
|
||||
attrs = {'class': "select_single"}
|
||||
return super(SelectSingle, self).render(name, value, attrs)
|
||||
|
||||
class SelectMultiple(Select):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
attrs['class'] = "select_multiple"
|
||||
else:
|
||||
attrs = {'class': "select_multiple"}
|
||||
return super(SelectMultiple, self).render(name, value, attrs)
|
||||
|
||||
class SelectDate(DateInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
attrs['class'] = "select_date"
|
||||
else:
|
||||
attrs = {'class': "select_date"}
|
||||
return super(SelectDate, self).render(name, value, attrs)
|
||||
|
||||
class SelectFile(TextInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
attrs['class'] = "select_file"
|
||||
else:
|
||||
attrs = {'class': "select_file"}
|
||||
output = '%(content)s<div name="%(name)s" class="choose_file_widget" title="%(title)s"></div>' % {
|
||||
'content': super(SelectFile, self).render(name, value, attrs),
|
||||
'title': _("Choose file"),
|
||||
'name': name,
|
||||
}
|
||||
output += '<span name="' + name + '" class="choose_file_button">' + _("Choose file") + '</span>'
|
||||
print(output)
|
||||
return output
|
||||
|
||||
# Forms
|
||||
|
||||
class RegisteringForm(UserCreationForm):
|
||||
error_css_class = 'error'
|
||||
@ -22,6 +67,69 @@ class RegisteringForm(UserCreationForm):
|
||||
user.save()
|
||||
return user
|
||||
|
||||
class UserProfileForm(forms.ModelForm):
|
||||
"""
|
||||
Form handling the user profile, managing the files
|
||||
This form is actually pretty bad and was made in the rush before the migration. It should be refactored.
|
||||
TODO: refactor this form
|
||||
"""
|
||||
class Meta:
|
||||
model = User
|
||||
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',
|
||||
'promo', 'forum_signature']
|
||||
widgets = {
|
||||
'date_of_birth': SelectDate,
|
||||
'profile_pict': forms.ClearableFileInput,
|
||||
'avatar_pict': forms.ClearableFileInput,
|
||||
'scrub_pict': forms.ClearableFileInput,
|
||||
}
|
||||
labels = {
|
||||
'profile_pict': _("Profile: you need to be visible on the picture, in order to be recognized (e.g. by the barmen)"),
|
||||
'avatar_pict': _("Avatar: used on the forum"),
|
||||
'scrub_pict': _("Scrub: let other know how your scrub looks like!"),
|
||||
}
|
||||
|
||||
def __init__(self, *arg, **kwargs):
|
||||
super(UserProfileForm, self).__init__(*arg, **kwargs)
|
||||
|
||||
def full_clean(self):
|
||||
super(UserProfileForm, self).full_clean()
|
||||
|
||||
def generate_name(self, field_name, f):
|
||||
field_name = field_name[:-4]
|
||||
return field_name + str(self.instance.id) + "." + f.content_type.split('/')[-1]
|
||||
|
||||
def process(self, files):
|
||||
avatar = self.instance.avatar_pict
|
||||
profile = self.instance.profile_pict
|
||||
scrub = self.instance.scrub_pict
|
||||
self.full_clean()
|
||||
self.cleaned_data['avatar_pict'] = avatar
|
||||
self.cleaned_data['profile_pict'] = profile
|
||||
self.cleaned_data['scrub_pict'] = scrub
|
||||
parent = SithFile.objects.filter(parent=None, name="profiles").first()
|
||||
for field,f in files:
|
||||
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:
|
||||
if not (f.content_type == "image/jpeg" or
|
||||
f.content_type == "image/png" or
|
||||
f.content_type == "image/gif"):
|
||||
raise ValidationError(_("Bad image format, only jpeg, png, and gif are accepted"))
|
||||
old = SithFile.objects.filter(parent=parent, name=new_file.name).first()
|
||||
if old:
|
||||
old.delete()
|
||||
new_file.clean()
|
||||
new_file.save()
|
||||
self.cleaned_data[field] = new_file
|
||||
self._errors.pop(field, None)
|
||||
except ValidationError as e:
|
||||
self._errors.pop(field, None)
|
||||
self.add_error(field, _("Error uploading file %(file_name)s: %(msg)s") %
|
||||
{'file_name': f, 'msg': str(e.message)})
|
||||
self._post_clean()
|
||||
|
||||
class UserPropForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
@ -52,13 +160,3 @@ class PagePropForm(forms.ModelForm):
|
||||
self.fields['edit_groups'].required = False
|
||||
self.fields['view_groups'].required = False
|
||||
|
||||
class SelectFile(Select):
|
||||
def render(self, name, value, attrs=None):
|
||||
output = '<span class="choose_file_widget" title="%(title)s">%(content)s</span>' % {
|
||||
'title': _("Choose file"),
|
||||
'content': super(SelectFile, self).render(name, value, attrs),
|
||||
}
|
||||
output += '<span class="choose_file_button">' + _("Choose file") + '</span>'
|
||||
print(output)
|
||||
return output
|
||||
|
||||
|
@ -11,7 +11,7 @@ from django.conf import settings
|
||||
import logging
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin
|
||||
from core.views.forms import RegisteringForm, UserPropForm
|
||||
from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm
|
||||
from core.models import User
|
||||
|
||||
def login(request):
|
||||
@ -115,7 +115,31 @@ class UserUpdateProfileView(CanEditMixin, UpdateView):
|
||||
model = User
|
||||
pk_url_kwarg = "user_id"
|
||||
template_name = "core/user_edit.jinja"
|
||||
fields = ('first_name', 'last_name', 'nick_name', 'email', 'date_of_birth', )
|
||||
form_class = UserProfileForm
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
self.form = self.get_form()
|
||||
if self.form.instance.profile_pict and not request.user.is_in_group(settings.SITH_MAIN_BOARD_GROUP):
|
||||
self.form.fields.pop('profile_pict', None)
|
||||
return self.render_to_response(self.get_context_data(form=self.form))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
self.form = self.get_form()
|
||||
if self.form.instance.profile_pict and not request.user.is_in_group(settings.SITH_MAIN_BOARD_GROUP):
|
||||
self.form.fields.pop('profile_pict', None)
|
||||
files = request.FILES.items()
|
||||
self.form.process(files)
|
||||
if request.user.is_authenticated() and request.user.can_edit(self.object) and self.form.is_valid():
|
||||
return super(UserUpdateProfileView, self).form_valid(self.form)
|
||||
return self.form_invalid(self.form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(UserUpdateProfileView, self).get_context_data(**kwargs)
|
||||
kwargs['profile'] = self.form.instance
|
||||
kwargs['form'] = self.form
|
||||
return kwargs
|
||||
|
||||
class UserUpdateGroupView(CanEditPropMixin, UpdateView):
|
||||
"""
|
||||
|