Add Forum

This commit is contained in:
Skia 2017-01-21 03:42:06 +01:00
parent fcaa740710
commit ea52462217
19 changed files with 449 additions and 1 deletions

View File

@ -18,6 +18,7 @@ from subscription.models import Subscription
from counter.models import Customer, ProductType, Product, Counter from counter.models import Customer, ProductType, Product, Counter
from com.models import Sith, Weekmail from com.models import Sith, Weekmail
from election.models import Election, Role, Candidature, ElectionList from election.models import Election, Role, Candidature, ElectionList
from forum.models import Forum, ForumMessage
class Command(BaseCommand): class Command(BaseCommand):
@ -429,3 +430,13 @@ Welcome to the wiki page!
cand = Candidature(role=pres, user=sli, election_list=listeT, program="En fait j'aime pas l'info, je voulais faire GMC") cand = Candidature(role=pres, user=sli, election_list=listeT, program="En fait j'aime pas l'info, je voulais faire GMC")
cand.save() cand.save()
# Forum
room = Forum(name="Salon de discussions", description="Pour causer de tout", is_category=True)
room.save()
Forum(name="AE", description="Réservé au bureau AE", parent=room).save()
Forum(name="BdF", description="Réservé au bureau BdF", parent=room).save()
Forum(name="Hall de discussions", description="Pour toutes les discussions", parent=room).save()
various = Forum(name="Divers", description="Pour causer de rien", is_category=True)
various.save()
Forum(name="Promos", description="Réservé aux Promos", parent=various).save()

View File

@ -91,7 +91,7 @@
<a href="https://ae.utbm.fr/matmatronch/">{% trans %}Matmatronch{% endtrans %}</a> <a href="https://ae.utbm.fr/matmatronch/">{% trans %}Matmatronch{% endtrans %}</a>
<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('sas:main') }}">{% trans %}SAS{% endtrans %}</a> <a href="{{ url('sas:main') }}">{% trans %}SAS{% endtrans %}</a>
<a href="https://ae.utbm.fr/forum2/">{% trans %}Forum{% endtrans %}</a> <a href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a>
<a href="{{ url('core:page', "services") }}">{% trans %}Services{% endtrans %}</a> <a href="{{ url('core:page', "services") }}">{% trans %}Services{% endtrans %}</a>
<a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a> <a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a>
<a href="https://ae.utbm.fr/article.php?name=liens">{% trans %}Sponsors{% endtrans %}</a> <a href="https://ae.utbm.fr/article.php?name=liens">{% trans %}Sponsors{% endtrans %}</a>

0
forum/__init__.py Normal file
View File

3
forum/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0019_preferences_receive_weekmail'),
]
operations = [
migrations.CreateModel(
name='Forum',
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
('name', models.CharField(verbose_name='name', max_length=64)),
('description', models.CharField(default='', verbose_name='description', max_length=256)),
('is_category', models.BooleanField(default=False, verbose_name='is a category')),
('edit_groups', models.ManyToManyField(to='core.Group', blank=True, related_name='editable_forums')),
('owner_group', models.ForeignKey(to='core.Group', default=4, related_name='owned_forums')),
('parent', models.ForeignKey(blank=True, null=True, to='forum.Forum', related_name='children')),
('view_groups', models.ManyToManyField(to='core.Group', blank=True, related_name='viewable_forums')),
],
),
migrations.CreateModel(
name='ForumMessage',
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
('title', models.CharField(default='', blank=True, verbose_name='title', max_length=64)),
('message', models.TextField(verbose_name='message', default='')),
('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='forum_messages')),
],
),
migrations.CreateModel(
name='ForumTopic',
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='forum_topics')),
('forum', models.ForeignKey(to='forum.Forum', related_name='topics')),
],
),
migrations.AddField(
model_name='forummessage',
name='topic',
field=models.ForeignKey(to='forum.ForumTopic', related_name='messages'),
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('forum', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='forumtopic',
name='title',
field=models.CharField(verbose_name='title', max_length=64, default=''),
),
]

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('forum', '0002_forumtopic_title'),
]
operations = [
migrations.AlterModelOptions(
name='forumtopic',
options={'ordering': ['messages__date', '-id']},
),
migrations.AddField(
model_name='forummessage',
name='date',
field=models.DateTimeField(verbose_name='date', default=django.utils.timezone.now),
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('forum', '0003_auto_20170121_0311'),
]
operations = [
migrations.AddField(
model_name='forumtopic',
name='description',
field=models.CharField(max_length=256, verbose_name='description', default=''),
),
]

View File

79
forum/models.py Normal file
View File

@ -0,0 +1,79 @@
from django.db import models
from django.core import validators
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.core.urlresolvers import reverse
from django.utils import timezone
from core.models import User, MetaGroup, Group, SithFile
class Forum(models.Model):
"""
The Forum class, made as a tree to allow nice tidy organization
"""
name = models.CharField(_('name'), max_length=64)
description = models.CharField(_('description'), max_length=256, default="")
is_category = models.BooleanField(_('is a category'), default=False)
parent = models.ForeignKey('Forum', related_name='children', null=True, blank=True)
owner_group = models.ForeignKey(Group, related_name="owned_forums",
default=settings.SITH_GROUP_COM_ADMIN_ID)
edit_groups = models.ManyToManyField(Group, related_name="editable_forums", blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_forums", blank=True)
def check_loop(self):
"""Raise a validation error when a loop is found within the parent list"""
objs = []
cur = self
while cur.parent is not None:
if cur in objs:
raise ValidationError(_('You can not make loops in forums'))
objs.append(cur)
cur = cur.parent
def clean(self):
self.check_loop()
def __str__(self):
return "%s" % (self.name)
def get_absolute_url(self):
return reverse('forum:view_forum', kwargs={'forum_id': self.id})
def get_parent_list(self):
l = []
p = self.parent
while p is not None:
l.append(p)
p = p.parent
return l
class ForumTopic(models.Model):
forum = models.ForeignKey(Forum, related_name='topics')
author = models.ForeignKey(User, related_name='forum_topics')
title = models.CharField(_("title"), default="", max_length=64)
description = models.CharField(_('description'), max_length=256, default="")
class Meta:
ordering = ['-id']
def get_absolute_url(self):
return reverse('forum:view_topic', kwargs={'topic_id': self.id})
class ForumMessage(models.Model):
"""
"A ForumMessage object is a message in the forum" Cpt. Obvious
"""
topic = models.ForeignKey(ForumTopic, related_name='messages')
author = models.ForeignKey(User, related_name='forum_messages')
title = models.CharField(_("title"), default="", max_length=64, blank=True)
message = models.TextField(_("message"), default="")
date = models.DateTimeField(_('date'), default=timezone.now)
class Meta:
ordering = ['-id']
def get_absolute_url(self):
return self.topic.get_absolute_url()

View File

@ -0,0 +1,44 @@
{% extends "core/base.jinja" %}
{% from 'forum/macros.jinja' import display_forum %}
{% block head %}
{{ super() }}
<style type="text/css" media="all">
.topic {
border: solid skyblue 1px;
padding: 2px;
margin: 2px;
}
.forum {
background: lightblue;
padding: 2px;
margin: 2px;
}
.category {
background: skyblue;
}
</style>
{% endblock %}
{% block content %}
<p>{{ forum.get_parent_list() }}</p>
<h3>{{ forum.name }}</h3>
<a href="{{ url('forum:new_forum') }}?parent={{ forum.id }}">New forum</a>
{% for f in forum.children.all() %}
{{ display_forum(f) }}
{% endfor %}
{% for t in forum.topics.all() %}
<div class="topic">
<p>
<a href="{{ url('forum:view_topic', topic_id=t.id) }}">View</a>
<a href="{{ url('forum:edit_topic', topic_id=t.id) }}">Edit</a>
</p>
<h5>{{ t.title }}</h5>
<p>{{ t.description }}</p>
</div>
{% endfor %}
<a href="{{ url('forum:new_topic', forum_id=forum.id) }}">New topic</a>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% macro display_forum(forum) %}
<div class="forum {% if forum.is_category %}category{% endif %}">
<p>
{% if not forum.is_category %}
<a href="{{ url('forum:view_forum', forum_id=forum.id) }}">View</a>
{% endif %}
<a href="{{ url('forum:edit_forum', forum_id=forum.id) }}">Edit</a>
</p>
<h5>{{ forum.name }}</h5>
<p>{{ forum.description }}</p>
</div>
{% endmacro %}

View File

@ -0,0 +1,33 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link %}
{% from 'forum/macros.jinja' import display_forum %}
{% block head %}
{{ super() }}
<style type="text/css" media="all">
.forum {
background: lightblue;
padding: 2px;
margin: 2px;
}
.category {
background: skyblue;
}
</style>
{% endblock %}
{% block content %}
<h3>{% trans %}Forum{% endtrans %}</h3>
<a href="{{ url('forum:new_forum') }}">New forum</a>
{% for f in forum_list %}
<div style="padding: 4px; margin: 4px">
{{ display_forum(f) }}
{% for c in f.children.all() %}
{{ display_forum(c) }}
{% endfor %}
</div>
{% endfor %}
{% endblock %}

View File

@ -0,0 +1,38 @@
{% extends "core/base.jinja" %}
{% block head %}
{{ super() }}
<style type="text/css" media="all">
.topic {
border: solid skyblue 1px;
padding: 2px;
margin: 2px;
}
.forum {
background: lightblue;
padding: 2px;
margin: 2px;
}
.category {
background: skyblue;
}
</style>
{% endblock %}
{% block content %}
<h3>{{ topic.title }}</h3>
<p>{{ topic.description }}</p>
<p><a href="{{ url('forum:new_message', topic_id=topic.id) }}">Reply</a></p>
{% for m in topic.messages.all() %}
<div>
<h5>{{ m.title }}</h5>
<p><strong>{{ m.author.get_display_name() }}</strong> - {{ m.date|date(DATETIME_FORMAT) }}
{{ m.date|time(DATETIME_FORMAT) }} -
<a href="{{ url('forum:new_message', topic_id=topic.id) }}?quote_id={{ m.id }}">Reply as quote</a></p>
<p>{{ m.message|markdown }}</p>
</div>
{% endfor %}
{% endblock %}

3
forum/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

16
forum/urls.py Normal file
View File

@ -0,0 +1,16 @@
from django.conf.urls import url, include
from forum.views import *
urlpatterns = [
url(r'^$', ForumMainView.as_view(), name='main'),
url(r'^new_forum$', ForumCreateView.as_view(), name='new_forum'),
url(r'^(?P<forum_id>[0-9]+)$', ForumDetailView.as_view(), name='view_forum'),
url(r'^(?P<forum_id>[0-9]+)/edit$', ForumEditView.as_view(), name='edit_forum'),
url(r'^(?P<forum_id>[0-9]+)/new_topic$', ForumTopicCreateView.as_view(), name='new_topic'),
url(r'^topic/(?P<topic_id>[0-9]+)$', ForumTopicDetailView.as_view(), name='view_topic'),
url(r'^topic/(?P<topic_id>[0-9]+)/edit$', ForumTopicEditView.as_view(), name='edit_topic'),
url(r'^topic/(?P<topic_id>[0-9]+)/new_message$', ForumMessageCreateView.as_view(), name='new_message'),
# url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
]

92
forum/views.py Normal file
View File

@ -0,0 +1,92 @@
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse, reverse_lazy
from django.utils import timezone
from django.conf import settings
from django import forms
from django.core.exceptions import PermissionDenied
from forum.models import Forum, ForumMessage, ForumTopic
class ForumMainView(ListView):
queryset = Forum.objects.filter(parent=None)
template_name = "forum/main.jinja"
class ForumCreateView(CreateView):
model = Forum
fields = ['name', 'parent', 'is_category', 'owner_group', 'edit_groups', 'view_groups']
template_name = "core/create.jinja"
def get_initial(self):
init = super(ForumCreateView, self).get_initial()
parent = Forum.objects.filter(id=self.request.GET['parent']).first()
init['parent'] = parent
return init
class ForumEditView(UpdateView):
model = Forum
pk_url_kwarg = "forum_id"
fields = ['name', 'parent', 'is_category', 'owner_group', 'edit_groups', 'view_groups']
template_name = "core/edit.jinja"
success_url = reverse_lazy('forum:main')
class ForumDetailView(DetailView):
model = Forum
template_name = "forum/forum.jinja"
pk_url_kwarg = "forum_id"
class ForumTopicCreateView(CreateView):
model = ForumTopic
fields = ['title']
template_name = "core/create.jinja"
def dispatch(self, request, *args, **kwargs):
self.forum = get_object_or_404(Forum, id=self.kwargs['forum_id'], is_category=False)
if not request.user.can_view(self.forum):
raise PermissionDenied
return super(ForumTopicCreateView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
form.instance.forum = self.forum
form.instance.author = self.request.user
return super(ForumTopicCreateView, self).form_valid(form)
class ForumTopicEditView(UpdateView):
model = ForumTopic
fields = ['title']
pk_url_kwarg = "topic_id"
template_name = "core/edit.jinja"
class ForumTopicDetailView(DetailView):
model = ForumTopic
pk_url_kwarg = "topic_id"
template_name = "forum/topic.jinja"
context_object_name = "topic"
class ForumMessageCreateView(CreateView):
model = ForumMessage
fields = ['title', 'message']
template_name = "core/create.jinja"
def dispatch(self, request, *args, **kwargs):
self.topic = get_object_or_404(ForumTopic, id=self.kwargs['topic_id'])
if not request.user.can_view(self.topic):
raise PermissionDenied
return super(ForumMessageCreateView, self).dispatch(request, *args, **kwargs)
def get_initial(self):
init = super(ForumMessageCreateView, self).get_initial()
try:
init['message'] = "\n".join([
" > " + line for line in ForumMessage.objects.filter(id=self.request.GET['quote_id']).first().message.split('\n')
])
except: pass
return init
def form_valid(self, form):
form.instance.topic = self.topic
form.instance.author = self.request.user
return super(ForumMessageCreateView, self).form_valid(form)

View File

@ -59,6 +59,7 @@ INSTALLED_APPS = (
'sas', 'sas',
'com', 'com',
'election', 'election',
'forum',
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (

View File

@ -40,6 +40,7 @@ urlpatterns = [
url(r'^sas/', include('sas.urls', namespace="sas", app_name="sas")), url(r'^sas/', include('sas.urls', namespace="sas", app_name="sas")),
url(r'^api/v1/', include('api.urls', namespace="api", app_name="api")), 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'^election/', include('election.urls', namespace="election", app_name="election")),
url(r'^forum/', include('forum.urls', namespace="forum", app_name="forum")),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^ajax_select/', include(ajax_select_urls)), url(r'^ajax_select/', include(ajax_select_urls)),
url(r'^i18n/', include('django.conf.urls.i18n')), url(r'^i18n/', include('django.conf.urls.i18n')),