Add a webchat

This commit is contained in:
Rodolphe Breard 2019-07-23 15:16:03 +02:00
parent b3154265c1
commit 5e7b176e39
23 changed files with 175 additions and 100 deletions

View file

@ -73,6 +73,10 @@ You can set the following variables in the `.env` file:
* `KHAGANAT_NSFW_TAGS`: Coma-separated list of words that triggers the content warning in logs, default is `\#nsfw`.
* `KHAGANAT_NSFW_NAME`: Name of the cookie holding the NSFW allowance, default is `nsfw_allowed`.
* `KHAGANAT_INTERNAL_IPS`: List of IP considered as internal, coma separated, default is `127.0.0.1,::1`.
* `KHAGANAT_XMPP_BOSH_URL`: URL of the BOSH server used to contact the XMPP server. Default is empty.
* `KHAGANAT_XMPP_JID`: The JID to use for the XMPP session. Default is empty.
* `KHAGANAT_XMPP_ROOMS`: List of rooms to automatically join when connecting. Default is empty.
* `KHAGANAT_XMPP_WEBSOCKET_URL`: URL of the websocket server used to contact the XMPP server. Default is empty.
## Quick update

7
chat/admin.py Normal file
View file

@ -0,0 +1,7 @@
from django.contrib import admin
from .models import LogSource
class LogSourceAdmin(admin.ModelAdmin):
list_display = ('name', 'slug', 'hidden')
admin.site.register(LogSource, LogSourceAdmin)

View file

@ -2,4 +2,4 @@ from django.apps import AppConfig
class LogsConfig(AppConfig):
name = 'logs'
name = 'chat'

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-04 19:45+0100\n"
"POT-Creation-Date: 2019-07-23 15:09+0200\n"
"PO-Revision-Date: 2018-01-27 18:35+0100\n"
"Last-Translator: Khaganat <assoc@khaganat.net>\n"
"Language-Team: Khaganat <assoc@khaganat.net>\n"
@ -16,36 +16,36 @@ msgstr ""
msgid "date"
msgstr ""
#: templates/logs/entries.html:10 templates/logs/entries.html:21
msgid "logs_back"
msgstr "Back"
#: templates/chat/chat_conversejs.html:15
msgid "chat_title"
msgstr "Khaganat's chat room"
#: templates/logs/index.html:4 templates/logs/index.html:8
#: templates/chat/entries.html:4 templates/chat/entries.html:11
msgid "logs_title"
msgstr "Khaganat's logs"
#: templates/logs/index.html:32
#: templates/chat/entries.html:21
msgid "logs_no_logs_available"
msgstr "No logs available."
#: templates/chat/entries.html:33
msgid "go"
msgstr ""
#: templates/logs/index.html:36
#: templates/chat/entries.html:40
#, python-format
msgid "%(name)s is hidden."
msgstr ""
#: templates/logs/index.html:37
#: templates/chat/entries.html:41
msgid "show"
msgstr ""
#: templates/logs/index.html:39
#: templates/chat/entries.html:43
#, python-format
msgid "%(name)s is published."
msgstr ""
#: templates/logs/index.html:40
#: templates/chat/entries.html:44
msgid "hide"
msgstr ""
#: templates/logs/index.html:51
msgid "logs_no_logs_available"
msgstr "No logs available."

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-04 19:45+0100\n"
"POT-Creation-Date: 2019-07-23 15:09+0200\n"
"PO-Revision-Date: 2018-01-27 18:35+0100\n"
"Last-Translator: Khaganat <assoc@khaganat.net>\n"
"Language-Team: Khaganat <assoc@khaganat.net>\n"
@ -16,36 +16,39 @@ msgstr ""
msgid "date"
msgstr ""
#: templates/logs/entries.html:10 templates/logs/entries.html:21
msgid "logs_back"
msgstr "Retour"
#: templates/chat/chat_conversejs.html:15
msgid "chat_title"
msgstr "Salon de discussion de Khaganat"
#: templates/logs/index.html:4 templates/logs/index.html:8
#: templates/chat/entries.html:4 templates/chat/entries.html:11
msgid "logs_title"
msgstr "Journaux de conversation de Khaganat"
#: templates/logs/index.html:32
#: templates/chat/entries.html:21
msgid "logs_no_logs_available"
msgstr "Aucun journal de conversation disponible."
#: templates/chat/entries.html:33
msgid "go"
msgstr ""
#: templates/logs/index.html:36
#: templates/chat/entries.html:40
#, python-format
msgid "%(name)s is hidden."
msgstr "%(name)s est masqué."
#: templates/logs/index.html:37
#: templates/chat/entries.html:41
msgid "show"
msgstr "montrer"
#: templates/logs/index.html:39
#: templates/chat/entries.html:43
#, python-format
msgid "%(name)s is published."
msgstr "%(name)s est publié."
#: templates/logs/index.html:40
#: templates/chat/entries.html:44
msgid "hide"
msgstr "masquer"
#: templates/logs/index.html:51
msgid "logs_no_logs_available"
msgstr "Aucun journal de conversation disponible."
#~ msgid "logs_back"
#~ msgstr "Retour"

View file

@ -1,5 +1,5 @@
from django.core.management.base import BaseCommand, CommandError
from logs.models import Entry
from chat.models import Entry
class Command(BaseCommand):

View file

@ -1,4 +1,4 @@
# Generated by Django 2.0.1 on 2018-01-27 20:08
# Generated by Django 2.2.3 on 2019-07-23 09:50
from django.db import migrations, models
import django.db.models.deletion
@ -13,17 +13,7 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='Entry',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('hidden', models.BooleanField(default=False)),
('created', models.DateTimeField()),
('nick', models.CharField(max_length=128)),
('content', models.CharField(max_length=2048)),
],
),
migrations.CreateModel(
name='Source',
name='LogSource',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128)),
@ -31,9 +21,15 @@ class Migration(migrations.Migration):
('hidden', models.BooleanField(default=False)),
],
),
migrations.AddField(
model_name='entry',
name='source',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='logs.Source'),
migrations.CreateModel(
name='LogEntry',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('hidden', models.BooleanField(default=False)),
('created', models.DateTimeField()),
('nick', models.CharField(max_length=128)),
('content', models.CharField(max_length=2048)),
('source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='chat.LogSource')),
],
),
]

View file

@ -1,7 +1,7 @@
from django.db import models
class Source(models.Model):
class LogSource(models.Model):
name = models.CharField(max_length=128)
slug = models.CharField(max_length=128, unique=True)
hidden = models.BooleanField(default=False)
@ -10,8 +10,8 @@ class Source(models.Model):
return self.name
class Entry(models.Model):
source = models.ForeignKey(Source, on_delete=models.CASCADE)
class LogEntry(models.Model):
source = models.ForeignKey(LogSource, on_delete=models.CASCADE)
hidden = models.BooleanField(default=False)
created = models.DateTimeField()
nick = models.CharField(max_length=128)

View file

@ -0,0 +1,50 @@
{% extends "khaganat/base.html" %}
{% load i18n %}
{% block headers %}
<!-- TODO: build it instead of using a CDN -->
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/4.2.0/css/converse.min.css">
<script src="https://cdn.conversejs.org/4.2.0/dist/converse.min.js" charset="utf-8"></script>
<style>
#converse-container {
height: 800px; // TODO: change this!
}
</style>
{% endblock %}
{% block title %}{% trans "chat_title" %}{% endblock %}
{% block content %}
<div class="content-bloc">
<div id="converse-container">
<div id="conversejs"></div>
</div>
</div>
{% endblock %}
{% block endscript %}
<script>
converse.initialize({
allow_contact_removal: false,
allow_contact_requests: false,
allow_logout: false,
allow_muc: true,
allow_muc_invitations: false,
allow_registration: false,
anonymous: true,
authentication: 'anonymous',
auto_join_rooms: [
{% for name in rooms %}'{{name}}',{% endfor %}
],
auto_login: true,
{% if bosh_url %}bosh_service_url: '{{ bosh_url }}',{% endif %}
debug: {{ debug|lower }},
hide_open_bookmarks: false,
{% if jid %}jid: '{{ jid }}',{% endif %}
show_controlbox_by_default: true,
sticky_controlbox: true,
view_mode: 'embedded',
{% if websocket_url %}websocket_url: '{{ websocket_url }}',{% endif %}
});
</script>
{% endblock %}

24
chat/urls.py Normal file
View file

@ -0,0 +1,24 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.chat_view, name='chat'),
path('logs/', views.LogEntriesView.as_view(), name='log_index'),
path(
'logs/<slug:source>/<int:year>/<int:month>/<int:day>/',
views.LogEntriesView.as_view(),
name='log_day'
),
path(
'logs/switch/source/<int:pk>/',
views.switch_log_source,
name='log_switch_source'
),
path(
'logs/switch/entry/<int:pk>/',
views.switch_log_entry,
name='log_switch_entry'
),
path('logs/search/', views.log_search_view, name='log_search'),
]

View file

@ -1,5 +1,5 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import redirect, get_object_or_404
from django.shortcuts import redirect, render, get_object_or_404
from django.db.models.functions import TruncDate
from django.db.models import Count
from django.views import generic
@ -7,12 +7,22 @@ from django.conf import settings
from django.urls import reverse
from django.http import Http404
from nsfw import views as nsfw
from .models import Source, Entry
from .models import LogSource, LogEntry
from .forms import SearchForm
from utils import is_link_legit
import datetime
def chat_view(request):
ctx = {
'debug': settings.DEBUG,
'bosh_url': settings.KHAGANAT_XMPP_BOSH_URL,
'jid': settings.KHAGANAT_XMPP_JID,
'rooms': settings.KHAGANAT_XMPP_ROOMS,
'websocket_url': settings.KHAGANAT_XMPP_WEBSOCKET_URL,
}
return render(request, 'chat/chat_conversejs.html', ctx)
def _get_dates():
now = datetime.date.today()
start_date = now - datetime.timedelta(
@ -36,16 +46,16 @@ def _switch_hidden(request, obj, pk):
@staff_member_required
def switch_source(request, pk):
return _switch_hidden(request, Source, pk)
def switch_log_source(request, pk):
return _switch_hidden(request, LogSource, pk)
@staff_member_required
def switch_entry(request, pk):
return _switch_hidden(request, Entry, pk)
def switch_log_entry(request, pk):
return _switch_hidden(request, LogEntry, pk)
def search_view(request):
def log_search_view(request):
if request.method == 'POST':
form = SearchForm(request.POST)
if form.is_valid():
@ -59,8 +69,8 @@ def search_view(request):
raise Http404('No search parameters.')
class EntriesView(generic.ListView):
template_name = 'logs/entries.html'
class LogEntriesView(generic.ListView):
template_name = 'chat/entries.html'
context_object_name = 'entries'
filter_nsfw = False
@ -104,7 +114,7 @@ class EntriesView(generic.ListView):
nb_max = settings.KHAGANAT_LOGS_MAX_DAYS
nb_max -= settings.KHAGANAT_LOGS_MIN_DAYS
lst = Entry.objects.filter(
lst = LogEntry.objects.filter(
source=source,
)
if not self.request.user.is_staff:
@ -123,12 +133,12 @@ class EntriesView(generic.ListView):
"""Return the current source."""
if self.kwargs.get('source') is None:
return None
return Source.objects.get(slug=self.kwargs['source'])
return LogSource.objects.get(slug=self.kwargs['source'])
def get_sources(self):
"""Return available sources."""
now, start_date, end_date = _get_dates()
qs = Entry.objects.all()
qs = LogEntry.objects.all()
if not self.request.user.is_staff:
qs = qs.filter(
@ -175,13 +185,13 @@ class EntriesView(generic.ListView):
(now - dt).days < settings.KHAGANAT_LOGS_MIN_DAYS,
))
if out_of_bounds and not is_staff:
return Entry.objects.none()
return LogEntry.objects.none()
src = self.get_source()
if src is None:
return Entry.objects.none()
return LogEntry.objects.none()
if src.hidden and not is_staff:
return Entry.objects.none()
qs = Entry.objects.filter(
return LogEntry.objects.none()
qs = LogEntry.objects.filter(
source=src,
created__year=dt.year
).filter(

View file

@ -41,15 +41,15 @@ INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'khaganat.apps.KhaganatConfig',
'neluser.apps.NeluserConfig',
'pages.apps.PagesConfig',
'navbar.apps.NavbarConfig',
'static_extra.apps.KhaganatStaticFilesConfig',
'logs.apps.LogsConfig',
'nsfw.apps.NsfwConfig',
'npb.apps.NpbConfig',
'bulma',
'chat.apps.LogsConfig',
'khaganat.apps.KhaganatConfig',
'navbar.apps.NavbarConfig',
'neluser.apps.NeluserConfig',
'npb.apps.NpbConfig',
'nsfw.apps.NsfwConfig',
'pages.apps.PagesConfig',
'static_extra.apps.KhaganatStaticFilesConfig',
]
MIDDLEWARE = [
@ -196,3 +196,10 @@ if config('KHAGANAT_FORCE_HTTPS', default=False, cast=bool):
KHAGANAT_NSFW_TAGS = config('KHAGANAT_NSFW_TAGS', default='\\#nsfw', cast=Csv())
KHAGANAT_NSFW_NAME = config('KHAGANAT_NSFW_NAME', default='nsfw_allowed')
KHAGANAT_NSFW_OK = ('y', 'yes', 't', 'true', '1')
# Converse.js
KHAGANAT_XMPP_BOSH_URL = config('KHAGANAT_XMPP_BOSH_URL', default='') or None
KHAGANAT_XMPP_JID = config('KHAGANAT_XMPP_JID', default='') or None
KHAGANAT_XMPP_ROOMS = config('KHAGANAT_XMPP_ROOMS', default='', cast=Csv()) or []
KHAGANAT_XMPP_WEBSOCKET_URL = config('KHAGANAT_XMPP_WEBSOCKET_URL', default='') or None

View file

@ -58,5 +58,6 @@
</ul>
</div>
</footer>
{% block endscript %}{% endblock %}
</body>
</html>

View file

@ -29,6 +29,6 @@ urlpatterns += i18n_patterns(
path('account/', include('neluser.urls')),
path('page/', include('pages.urls')),
path('paste/', include('npb.urls', namespace='npb')),
path('logs/', include('logs.urls')),
path('chat/', include('chat.urls')),
path('nsfw/', include('nsfw.urls')),
)

View file

@ -1,4 +0,0 @@
from django.contrib import admin
from .models import Source
admin.site.register(Source)

View file

@ -1,23 +0,0 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.EntriesView.as_view(), name='log_index'),
path(
'<slug:source>/<int:year>/<int:month>/<int:day>/',
views.EntriesView.as_view(),
name='log_day'
),
path(
'switch/source/<int:pk>/',
views.switch_source,
name='log_switch_source'
),
path(
'switch/entry/<int:pk>/',
views.switch_entry,
name='log_switch_entry'
),
path('search/', views.search_view, name='log_search'),
]