Merge pull request #1626 from normanjaeckel/Chat

Added model, serializer and viewset for chat messages.
This commit is contained in:
Oskar Hahn 2015-09-07 17:34:43 +02:00
commit 4d14a1297d
8 changed files with 104 additions and 118 deletions

View File

@ -20,6 +20,7 @@ class CoreAppConfig(AppConfig):
from openslides.utils.rest_api import router
from .signals import setup_general_config
from .views import (
ChatMessageViewSet,
ConfigViewSet,
CustomSlideViewSet,
ProjectorViewSet,
@ -31,6 +32,7 @@ class CoreAppConfig(AppConfig):
# Register viewsets.
router.register('core/projector', ProjectorViewSet)
router.register('core/chatmessage', ChatMessageViewSet)
router.register('core/customslide', CustomSlideViewSet)
router.register('core/tag', TagViewSet)
router.register('core/config', ConfigViewSet, 'config')

View File

@ -1,110 +0,0 @@
from datetime import datetime
from django.conf import settings
from django.utils.html import urlize
from django.utils.importlib import import_module
from sockjs.tornado import SockJSConnection
class ChatboxSocketHandler(SockJSConnection):
"""
Websocket handler for the chatbox.
"""
clients = set()
def on_open(self, info):
"""
Checks connecting user and adds his client to the clients list.
"""
from openslides.users.models import User
# get the session (compatible with other auth-backends)
engine = import_module(settings.SESSION_ENGINE)
try:
session_key = info.get_cookie(settings.SESSION_COOKIE_NAME).value
session_store = engine.SessionStore(session_key)
pk = session_store.get('_auth_user_id')
except AttributeError:
return False
try:
self.user = User.objects.get(pk=pk)
except User.DoesNotExist:
return False
if self.user.has_perm('core.can_use_chat'):
self.clients.add(self)
return True
else:
return False
def on_message(self, message):
"""
Sends the given message to all clients.
Also appends the message to the cache and removes old messages if there
are more than 100.
"""
if self.user.has_perm('core.can_use_chat') and message:
message_object = ChatMessage(person=self.user, message=message)
chat_messages.append(message_object)
if len(chat_messages) > 100:
chat_messages.pop(0)
self.broadcast(
self.clients,
'%s %s %s' % (message_object.html_time(),
message_object.html_person(),
urlize(message_object.message)))
def on_close(self):
"""
Removes client from the clients list.
"""
self.clients.remove(self)
class ChatMessage(object):
"""
Class for all chat messages. They are stored in the chat_messages object.
The argument person has to be a Person object, the argument message has to
be the message as string. The argument color can be a three-tuple of RGB
color values. Default is black (0, 0, 0).
"""
def __init__(self, person, message, color=None):
self.person = person
self.message = message
self.color = color or (0, 0, 0)
self.time = datetime.now()
def html_time(self):
"""
Returns the message time in html style.
"""
return '<small class="grey">%s</small>' % self.time.strftime('%H:%M')
def html_person(self):
"""
Returns the message sender name in html style.
"""
return "<span style='color:%(color)s;'>%(person)s:</span>" % {
'color': 'rgb(%d,%d,%d)' % self.color,
'person': self.person.clean_name}
chat_messages = []
"""
Cache with all messages during livetime of the server.
"""
def chat_messages_context_processor(request):
"""
Adds all chat messages to the request context as template context processor.
Returns None if the request user has no permission to use the chat.
"""
if request.user.has_perm('core.can_use_chat'):
value = chat_messages
else:
value = None
return {'chat_messages': value}

View File

@ -0,0 +1,35 @@
from django.conf import settings
from django.db import migrations, models
import openslides.utils.models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0004_clear_all_and_make_it_new'),
]
operations = [
migrations.CreateModel(
name='ChatMessage',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('message', models.TextField(verbose_name='Message')),
('timestamp', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL)),
],
options={
'permissions': (('can_use_chat', 'Can use the chat'),),
},
bases=(openslides.utils.models.RESTModelMixin, models.Model),
),
migrations.AlterModelOptions(
name='projector',
options={'permissions': (
('can_see_projector', 'Can see the projector'),
('can_manage_projector', 'Can manage the projector'),
('can_see_dashboard', 'Can see the dashboard'))},
),
]

View File

@ -1,3 +1,4 @@
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop
@ -50,8 +51,7 @@ class Projector(RESTModelMixin, models.Model):
permissions = (
('can_see_projector', ugettext_noop('Can see the projector')),
('can_manage_projector', ugettext_noop('Can manage the projector')),
('can_see_dashboard', ugettext_noop('Can see the dashboard')),
('can_use_chat', ugettext_noop('Can use the chat')))
('can_see_dashboard', ugettext_noop('Can see the dashboard')))
@property
def elements(self):
@ -154,3 +154,26 @@ class ConfigStore(models.Model):
class Meta:
permissions = (('can_manage_config', ugettext_noop('Can manage configuration')),)
class ChatMessage(RESTModelMixin, models.Model):
"""
Model for chat messages.
At the moment we only have one global chat room for managers.
"""
message = models.TextField(
verbose_name=ugettext_lazy('Message'))
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=ugettext_lazy('User'))
class Meta:
permissions = (
('can_use_chat', ugettext_noop('Can use the chat')),)
def __str__(self):
return 'Message {}'.format(self.timestamp)

View File

@ -1,6 +1,6 @@
from openslides.utils.rest_api import Field, ModelSerializer, ValidationError
from .models import CustomSlide, Projector, Tag
from .models import ChatMessage, CustomSlide, Projector, Tag
class JSONSerializerField(Field):
@ -51,3 +51,13 @@ class TagSerializer(ModelSerializer):
class Meta:
model = Tag
fields = ('id', 'name', )
class ChatMessageSerializer(ModelSerializer):
"""
Serializer for core.models.ChatMessage objects.
"""
class Meta:
model = ChatMessage
fields = ('id', 'message', 'timestamp', 'user', )
read_only_fields = ('user', )

View File

@ -28,8 +28,9 @@ from openslides.utils.rest_api import (
from .config import config
from .exceptions import ConfigError, ConfigNotFound
from .models import CustomSlide, Projector, Tag
from .models import ChatMessage, CustomSlide, Projector, Tag
from .serializers import (
ChatMessageSerializer,
CustomSlideSerializer,
ProjectorSerializer,
TagSerializer,
@ -414,6 +415,34 @@ class ConfigViewSet(ViewSet):
return Response({'key': key, 'value': value})
class ChatMessageViewSet(ModelViewSet):
"""
API endpoint for chat messages.
There are the following views: metadata, list, retrieve and create.
The views partial_update, update and destroy are disabled.
"""
queryset = ChatMessage.objects.all()
serializer_class = ChatMessageSerializer
def check_view_permissions(self):
"""
Returns True if the user has required permissions.
"""
# We do not want anonymous users to use the chat even the anonymous
# group has the permission core.can_use_chat.
return (self.action in ('metadata', 'list', 'retrieve', 'create') and
self.request.user.is_authenticated() and
self.request.user.has_perm('core.can_use_chat'))
def perform_create(self, serializer):
"""
Customized method to inject the request.user into serializer's save
method so that the request.user can be saved into the model field.
"""
serializer.save(user=self.request.user)
# Special API views
class UrlPatternsView(utils_views.APIView):

View File

@ -97,7 +97,6 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.core.context_processors.i18n',
'django.core.context_processors.static',
'openslides.core.chatbox.chat_messages_context_processor',
)
CACHES = {

View File

@ -134,8 +134,6 @@ def run_tornado(addr, port, *args, **kwargs):
app = WSGIContainer(get_wsgi_application())
# Collect urls
from openslides.core.chatbox import ChatboxSocketHandler
chatbox_socket_js_router = SockJSRouter(ChatboxSocketHandler, '/core/chatbox')
sock_js_router = SockJSRouter(OpenSlidesSockJSConnection, '/sockjs')
other_urls = [
(r"%s(.*)" % settings.STATIC_URL, DjangoStaticFileHandler),
@ -144,7 +142,7 @@ def run_tornado(addr, port, *args, **kwargs):
# Start the application
debug = settings.DEBUG
tornado_app = Application(sock_js_router.urls + chatbox_socket_js_router.urls + other_urls, autoreload=debug, debug=debug)
tornado_app = Application(sock_js_router.urls + other_urls, autoreload=debug, debug=debug)
server = HTTPServer(tornado_app)
server.listen(port=port, address=addr)
IOLoop.instance().start()