diff --git a/club/templates/club/club_tools.jinja b/club/templates/club/club_tools.jinja
index f28e4978..233ddc53 100644
--- a/club/templates/club/club_tools.jinja
+++ b/club/templates/club/club_tools.jinja
@@ -7,6 +7,11 @@
{% trans %}Counters:{% endtrans %}
diff --git a/core/templates/core/user_preferences.jinja b/core/templates/core/user_preferences.jinja
new file mode 100644
index 00000000..6cd1d180
--- /dev/null
+++ b/core/templates/core/user_preferences.jinja
@@ -0,0 +1,26 @@
+{% extends "core/base.jinja" %}
+
+{% block title %}
+{% trans %}Preferences{% endtrans %}
+{% endblock %}
+
+{% block content %}
+{% trans %}Preferences{% endtrans %}
+
+{% if matmat_form %}
+
+{% else %}
+Matmatronch tools
+{% endif %}
+{% endblock %}
+
+
+
diff --git a/core/views/user.py b/core/views/user.py
index d8e49d80..767a9868 100644
--- a/core/views/user.py
+++ b/core/views/user.py
@@ -46,6 +46,7 @@ from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm, Log
from core.models import User, SithFile, Preferences
from club.models import Club
from subscription.models import Subscription
+from matmat.views import UserMatmatForm
def login(request):
"""
@@ -427,7 +428,7 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
"""
model = User
pk_url_kwarg = "user_id"
- template_name = "core/edit.jinja"
+ template_name = "core/user_preferences.jinja"
form_class = modelform_factory(Preferences, fields=['receive_weekmail'])
context_object_name = "profile"
current_tab = "prefs"
@@ -446,6 +447,12 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
kwargs.update({'instance': pref})
return kwargs
+ def get_context_data(self, **kwargs):
+ kwargs = super(UserPreferencesView, self).get_context_data(**kwargs)
+ if not hasattr(self.object, 'matmat_user'):
+ kwargs['matmat_form'] = UserMatmatForm()
+ return kwargs
+
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
"""
Edit a user's groups
diff --git a/matmat/__init__.py b/matmat/__init__.py
new file mode 100644
index 00000000..55a358e2
--- /dev/null
+++ b/matmat/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding:utf-8 -*
+#
+# Copyright 2017
+# - Skia
+#
+# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
+# http://ae.utbm.fr.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License a published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
+# Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+
diff --git a/matmat/admin.py b/matmat/admin.py
new file mode 100644
index 00000000..63080be1
--- /dev/null
+++ b/matmat/admin.py
@@ -0,0 +1,29 @@
+# -*- coding:utf-8 -*
+#
+# Copyright 2017
+# - Skia
+#
+# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
+# http://ae.utbm.fr.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License a published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
+# Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+
+from django.contrib import admin
+
+from matmat.models import Matmat
+
+admin.site.register(Matmat)
diff --git a/matmat/migrations/0001_initial.py b/matmat/migrations/0001_initial.py
new file mode 100644
index 00000000..d10b1b50
--- /dev/null
+++ b/matmat/migrations/0001_initial.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+from django.conf import settings
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('club', '0007_auto_20170324_0917'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Matmat',
+ fields=[
+ ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
+ ('subscription_deadline', models.DateField(verbose_name='subscription deadline', default=django.utils.timezone.now, help_text='Before this date, users are allowed to subscribe to this Matmatronch. After this date, users subscribed will be allowed to comment on each other.')),
+ ('comments_deadline', models.DateField(verbose_name='comments deadline', default=django.utils.timezone.now, help_text="After this date, users won't be able to make comments anymore")),
+ ('club', models.OneToOneField(related_name='matmat', to='club.Club')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MatmatComment',
+ fields=[
+ ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
+ ('author', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, verbose_name='matmat', to=settings.AUTH_USER_MODEL, related_name='users', null=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MatmatUser',
+ fields=[
+ ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
+ ('matmat', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, verbose_name='matmat', to='matmat.Matmat', related_name='users', null=True)),
+ ('user', models.OneToOneField(related_name='matmat_user', verbose_name='matmat user', to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/matmat/migrations/__init__.py b/matmat/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/matmat/models.py b/matmat/models.py
new file mode 100644
index 00000000..460b3e93
--- /dev/null
+++ b/matmat/models.py
@@ -0,0 +1,99 @@
+# -*- coding:utf-8 -*
+#
+# Copyright 2017
+# - Skia
+#
+# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
+# http://ae.utbm.fr.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License a published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
+# Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from django.core.urlresolvers import reverse
+from django.conf import settings
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+
+from datetime import timedelta, date
+
+from core.models import User
+from club.models import Club
+
+class MatmatManager(models.Manager):
+ def get_queryset(self):
+ return super(MatmatManager, self).get_queryset()
+
+class AvailableMatmatManager(models.Manager):
+ def get_queryset(self):
+ return super(AvailableMatmatManager,
+ self).get_queryset().filter(subscription_deadline__gte=date.today())
+
+class Matmat(models.Model):
+ """
+ This is the main class, the Matmat itself.
+ It contains the deadlines for the users, and the link to the club that makes
+ its Matmatronch.
+ """
+ subscription_deadline = models.DateField(_('subscription deadline'),
+ default=timezone.now, help_text=_("Before this date, users are "
+ "allowed to subscribe to this Matmatronch. "
+ "After this date, users subscribed will be allowed to comment on each other."))
+ comments_deadline = models.DateField(_('comments deadline'),
+ default=timezone.now, help_text=_("After this date, users won't be "
+ "able to make comments anymore"))
+ club = models.OneToOneField(Club, related_name='matmat')
+
+ objects = MatmatManager()
+ availables = AvailableMatmatManager()
+
+ def __str__(self):
+ return str(self.club.name)
+
+ def clean(self):
+ if self.subscription_deadline > self.comments_deadline:
+ raise ValidationError(_("Closing the subscriptions after the "
+ "comments is definitively not a good idea."))
+
+ def get_absolute_url(self):
+ return reverse('matmat:detail', kwargs={'matmat_id': self.id})
+
+ def is_owned_by(self, user):
+ return user.is_owner(self.club)
+
+ def can_be_viewed_by(self, user):
+ return user.can_edit(self.club)
+
+class MatmatUser(models.Model):
+ """
+ This class is only here to avoid cross references between the core, club,
+ and matmat modules. It binds a User to a Matmat without needing to import
+ Matmat into the core.
+ """
+ user = models.OneToOneField(User, verbose_name=_("matmat user"), related_name='matmat_user')
+ matmat = models.ForeignKey(Matmat, verbose_name=_("matmat"), related_name='users', blank=True, null=True, on_delete=models.SET_NULL)
+
+class MatmatComment(models.Model):
+ """
+ This represent a comment given by someone to someone else in the same Matmat
+ instance.
+ """
+ author = models.ForeignKey(MatmatUser, verbose_name=_("author"), related_name='given_comments')
+ target = models.ForeignKey(MatmatUser, verbose_name=_("target"), related_name='received_comments')
+ content = models.TextField(_("content"), default="", max_length=400)
+ is_moderated = models.BooleanField(_("is moderated"), default=False)
+
diff --git a/matmat/templates/matmat/detail.jinja b/matmat/templates/matmat/detail.jinja
new file mode 100644
index 00000000..937177ba
--- /dev/null
+++ b/matmat/templates/matmat/detail.jinja
@@ -0,0 +1,19 @@
+{% extends 'core/base.jinja' %}
+
+{% block title %}
+{% trans club=object.club %}{{ club }}'s Matmatronch{% endtrans %}
+{% endblock %}
+
+{% block content %}
+ {% trans club=object.club %}{{ club }}'s Matmatronch{% endtrans %}
+ Edit
+ Subscription deadline: {{ object.subscription_deadline|date(DATETIME_FORMAT) }}
+ Comment deadline: {{ object.comments_deadline|date(DATETIME_FORMAT) }}
+ Export
+ People:
+
+ {% for u in object.users.all() %}
+ - {{ u.user.get_display_name() }} - Delete
+ {% endfor %}
+
+{% endblock %}
diff --git a/matmat/templates/matmat/user_matmat.jinja b/matmat/templates/matmat/user_matmat.jinja
new file mode 100644
index 00000000..d368cf6a
--- /dev/null
+++ b/matmat/templates/matmat/user_matmat.jinja
@@ -0,0 +1,25 @@
+{% extends "core/base.jinja" %}
+
+{% block title %}
+ {% trans user_name=user.get_display_name() %}{{ user_name }}'s Matmat{% endtrans %}
+{% endblock %}
+
+{% block content %}
+{% trans%}Matmatronch'{% endtrans %}
+{% if subscribe_form %}
+
+{% else %}
+{% trans matmat = user.matmat_user.matmat %}You are subscribed to the Matmatronch {{ matmat }}{% endtrans %}
+{% for u in user.matmat_user.matmat.users.all() %}
+
+
{{ u.user.get_display_name() }}
+
Comment
+
+{% endfor %}
+{% endif %}
+{% endblock %}
+
diff --git a/matmat/tests.py b/matmat/tests.py
new file mode 100644
index 00000000..de62cd38
--- /dev/null
+++ b/matmat/tests.py
@@ -0,0 +1,27 @@
+# -*- coding:utf-8 -*
+#
+# Copyright 2017
+# - Skia
+#
+# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
+# http://ae.utbm.fr.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License a published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
+# Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/matmat/urls.py b/matmat/urls.py
new file mode 100644
index 00000000..072dfa18
--- /dev/null
+++ b/matmat/urls.py
@@ -0,0 +1,36 @@
+# -*- coding:utf-8 -*
+#
+# Copyright 2017
+# - Skia
+#
+# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
+# http://ae.utbm.fr.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License a published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
+# Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+
+from django.conf.urls import url, include
+
+from matmat.views import *
+
+urlpatterns = [
+ url(r'^(?P[0-9]+)/new$', MatmatCreateView.as_view(), name='create'),
+ url(r'^(?P[0-9]+)/edit$', MatmatEditView.as_view(), name='edit'),
+ url(r'^(?P[0-9]+)/delete/(?P[0-9]+)$', MatmatDeleteUserView.as_view(), name='delete_user'),
+ url(r'^(?P[0-9]+)$', MatmatDetailView.as_view(), name='detail'),
+ url(r'^tools$', UserMatmatView.as_view(), name='user_tools'),
+]
+
diff --git a/matmat/views.py b/matmat/views.py
new file mode 100644
index 00000000..484905e2
--- /dev/null
+++ b/matmat/views.py
@@ -0,0 +1,118 @@
+# -*- coding:utf-8 -*
+#
+# Copyright 2017
+# - Skia
+#
+# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
+# http://ae.utbm.fr.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License a published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
+# Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+
+from django.shortcuts import render, get_object_or_404, redirect
+from django.views.generic import ListView, DetailView, RedirectView, TemplateView
+from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView, SingleObjectMixin
+from django.utils.translation import ugettext_lazy as _
+from django import forms
+
+from matmat.models import Matmat, MatmatUser
+from core.views.forms import SelectFile, SelectDate
+from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
+from core.models import User
+from club.models import Club
+
+class MatmatForm(forms.ModelForm):
+ class Meta:
+ model = Matmat
+ fields = ['subscription_deadline', 'comments_deadline']
+ widgets = {
+ 'subscription_deadline': SelectDate,
+ 'comments_deadline': SelectDate,
+ }
+
+class MatmatCreateView(CanEditPropMixin, CreateView):
+ """
+ Create a matmat for a club
+ """
+ model = Matmat
+ form_class = MatmatForm
+ template_name = 'core/create.jinja'
+
+ def post(self, request, *args, **kwargs):
+ """
+ Affect club
+ """
+ form = self.get_form()
+ if form.is_valid():
+ club = get_object_or_404(Club, id=self.kwargs['club_id'])
+ form.instance.club = club
+ ret = self.form_valid(form)
+ return ret
+ else:
+ return self.form_invalid(form)
+
+class MatmatEditView(CanEditPropMixin, UpdateView):
+ model = Matmat
+ form_class = MatmatForm
+ template_name = 'core/edit.jinja'
+ pk_url_kwarg = 'matmat_id'
+
+class MatmatDetailView(CanViewMixin, DetailView):
+ model = Matmat
+ template_name = 'matmat/detail.jinja'
+ pk_url_kwarg = 'matmat_id'
+
+class MatmatDeleteUserView(CanEditPropMixin, SingleObjectMixin, RedirectView):
+ model = Matmat
+ pk_url_kwarg = 'matmat_id'
+ permanent = False
+
+ def get(self, request, *args, **kwargs):
+ self.object = self.get_object()
+ user = get_object_or_404(MatmatUser, id=self.kwargs['user_id'])
+ user.delete()
+# See if we need to also delete the comments on the user, or if we keep them
+ return redirect(self.object.get_absolute_url())
+
+# User side
+class UserMatmatForm(forms.Form):
+ matmat = forms.ModelChoiceField(Matmat.availables.all(), required=False, label=_("Select matmatronch"),
+ help_text=_("This allows you to subscribe to a Matmatronch. "
+ "Be aware that you can subscribe only once, so don't play with that, "
+ "or you will expose yourself to the admins' wrath!"))
+
+class UserMatmatView(QuickNotifMixin, TemplateView):
+ """
+ Display a user's matmat tools
+ """
+ template_name = "core/user_matmat.jinja"
+
+ def post(self, request, *args, **kwargs):
+ self.form = UserMatmatForm(request.POST)
+ if self.form.is_valid():
+ matmat_user = MatmatUser(user=request.user,
+ matmat=self.form.cleaned_data['matmat'])
+ matmat_user.save()
+ self.quick_notif_list += ['qn_success']
+ return super(UserMatmatView, self).get(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ kwargs = super(UserMatmatView, self).get_context_data(**kwargs)
+ kwargs['user'] = self.request.user
+ if not hasattr(self.request.user, 'matmat_user'):
+ kwargs['subscribe_form'] = UserMatmatForm()
+ return kwargs
+
diff --git a/sith/settings.py b/sith/settings.py
index 9a5422a1..cf439721 100644
--- a/sith/settings.py
+++ b/sith/settings.py
@@ -86,6 +86,7 @@ INSTALLED_APPS = (
'election',
'forum',
'stock',
+ 'matmat',
)
MIDDLEWARE_CLASSES = (
diff --git a/sith/urls.py b/sith/urls.py
index 4ba65ca8..ae65cd1b 100644
--- a/sith/urls.py
+++ b/sith/urls.py
@@ -66,6 +66,7 @@ urlpatterns = [
url(r'^api/v1/', include('api.urls', namespace="api", app_name="api")),
url(r'^election/', include('election.urls', namespace="election", app_name="election")),
url(r'^forum/', include('forum.urls', namespace="forum", app_name="forum")),
+ url(r'^matmat/', include('matmat.urls', namespace="matmat", app_name="matmat")),
url(r'^admin/', include(admin.site.urls)),
url(r'^ajax_select/', include(ajax_select_urls)),
url(r'^i18n/', include('django.conf.urls.i18n')),