From f650f4225ecfa7652aeb066b8e695a8ce2a57359 Mon Sep 17 00:00:00 2001 From: Adrian Moennich Date: Tue, 7 Oct 2014 10:43:51 +0200 Subject: [PATCH] Chatroom management (and some more stuff) --- chat/indico_chat/blueprint.py | 16 ++- chat/indico_chat/controllers.py | 118 +++++++++++++++++- chat/indico_chat/forms.py | 42 +++++++ chat/indico_chat/models/chatrooms.py | 9 ++ chat/indico_chat/plugin.py | 13 +- chat/indico_chat/static/css/chat.scss | 17 ++- chat/indico_chat/static/js/chat.js | 25 +++- chat/indico_chat/templates/event_header.html | 6 +- chat/indico_chat/templates/manage_event.html | 71 +++++++++++ .../templates/manage_event_edit.html | 42 +++++++ chat/indico_chat/views.py | 7 +- chat/indico_chat/xmpp.py | 35 ++++++ 12 files changed, 385 insertions(+), 16 deletions(-) create mode 100644 chat/indico_chat/forms.py create mode 100644 chat/indico_chat/templates/manage_event.html create mode 100644 chat/indico_chat/templates/manage_event_edit.html create mode 100644 chat/indico_chat/xmpp.py diff --git a/chat/indico_chat/blueprint.py b/chat/indico_chat/blueprint.py index 24e5763..76d5880 100644 --- a/chat/indico_chat/blueprint.py +++ b/chat/indico_chat/blueprint.py @@ -18,7 +18,17 @@ from __future__ import unicode_literals from indico.core.plugins import IndicoPluginBlueprint -from indico_chat.controllers import RHChatEventPage +from indico_chat.controllers import (RHChatEventPage, RHChatManageEvent, RHChatManageEventModify, + RHChatManageEventRemove, RHChatManageEventCreate, RHChatManageEventAttach) -blueprint = IndicoPluginBlueprint('chat', 'indico_chat') -blueprint.add_url_rule('/event//chat-new', 'event-page', RHChatEventPage) +# TODO: s/chat-new/chat/ +blueprint = IndicoPluginBlueprint('chat', 'indico_chat', url_prefix='/event/') +blueprint.add_url_rule('/chat-new', 'event_page', RHChatEventPage) +blueprint.add_url_rule('/manage/chat-new/', 'manage_rooms', RHChatManageEvent) +blueprint.add_url_rule('/manage/chat-new//', 'manage_rooms_modify', RHChatManageEventModify, + methods=('GET', 'POST')) +blueprint.add_url_rule('/manage/chat-new//remove', 'manage_rooms_remove', RHChatManageEventRemove, + methods=('POST',)) +blueprint.add_url_rule('/manage/chat-new/create', 'manage_rooms_create', RHChatManageEventCreate, + methods=('GET', 'POST')) +blueprint.add_url_rule('/manage/chat-new/attach', 'manage_rooms_attach', RHChatManageEventAttach, methods=('POST',)) diff --git a/chat/indico_chat/controllers.py b/chat/indico_chat/controllers.py index 6fb9a4f..97ec914 100644 --- a/chat/indico_chat/controllers.py +++ b/chat/indico_chat/controllers.py @@ -16,16 +16,27 @@ from __future__ import unicode_literals +from flask import session, request, flash, redirect from flask_pluginengine import current_plugin +from indico.core.db import db +from indico.core.db.sqlalchemy.util.models import attrs_changed from indico.core.errors import IndicoError +from indico.core.plugins import url_for_plugin +from indico.web.forms.base import FormDefaults from MaKaC.webinterface.rh.conferenceDisplay import RHConferenceBaseDisplay +from MaKaC.webinterface.rh.conferenceModif import RHConferenceModifBase -from indico_chat.models.chatrooms import ChatroomEventAssociation -from indico_chat.views import WPChatEventPage +from indico_chat.forms import AddChatroomForm, EditChatroomForm +from indico_chat.models.chatrooms import ChatroomEventAssociation, Chatroom +from indico_chat.util import is_chat_admin +from indico_chat.views import WPChatEventPage, WPChatEventMgmt +from indico_chat.xmpp import create_room, update_room, delete_room class RHChatEventPage(RHConferenceBaseDisplay): + """Lists the public chatrooms in a conference""" + def _process(self): try: event_id = int(self._conf.id) @@ -41,3 +52,106 @@ class RHChatEventPage(RHConferenceBaseDisplay): return WPChatEventPage.render_template('event_page.html', self._conf, event_chatrooms=chatrooms, cols=cols, chat_links=current_plugin.settings.get('chat_links')) + +class RHChatManageEventBase(RHConferenceModifBase): + def _checkParams(self, params): + RHConferenceModifBase._checkParams(self, params) + try: + self.event_id = int(self._conf.id) + except ValueError: + raise IndicoError('This page is not available for legacy events.') + self.event = self._conf + + def _checkProtection(self): + if not is_chat_admin(session.user): + RHConferenceModifBase._checkProtection(self) + + +class RHChatManageEvent(RHChatManageEventBase): + """Lists the chatrooms of an event""" + + def _process(self): + chatrooms = ChatroomEventAssociation.find_all(ChatroomEventAssociation.event_id == self.event_id, + _eager='chatroom.events') + chatroom_filter = (~Chatroom.id.in_(x.chatroom_id for x in chatrooms)) if chatrooms else True + available_chatrooms = Chatroom.find_all(Chatroom.created_by_id == int(session.user.id), chatroom_filter) + return WPChatEventMgmt.render_template('manage_event.html', self._conf, event_chatrooms=chatrooms, + event=self.event, chat_links=current_plugin.settings.get('chat_links'), + available_chatrooms=available_chatrooms) + + +class RHChatManageEventModify(RHChatManageEventBase): + """Modifies an existing chatroom""" + + def _checkParams(self, params): + RHChatManageEventBase._checkParams(self, params) + self.event_chatroom = ChatroomEventAssociation.find_one(event_id=self.event_id, + chatroom_id=request.view_args['chatroom_id']) + self.chatroom = self.event_chatroom.chatroom + + def _process(self): + defaults = FormDefaults(self.chatroom) + for name in EditChatroomForm.event_specific_fields: + defaults[name] = getattr(self.event_chatroom, name) + form = EditChatroomForm(obj=defaults) + if form.validate_on_submit(): + form.populate_obj(self.event_chatroom, fields=form.event_specific_fields) + form.populate_obj(self.chatroom, skip=form.event_specific_fields) + if attrs_changed(self.chatroom, 'name', 'description', 'password'): + update_room(self.chatroom) + flash('Chatroom updated', 'success') + return redirect(url_for_plugin('.manage_rooms', self.event)) + return WPChatEventMgmt.render_template('manage_event_edit.html', self._conf, form=form, + event_chatroom=self.event_chatroom) + + +class RHChatManageEventCreate(RHChatManageEventBase): + """Creates a new chatroom for an event""" + + def _process(self): + form = AddChatroomForm() + if form.validate_on_submit(): + chatroom = Chatroom(created_by_user=session.user) + event_chatroom = ChatroomEventAssociation(event_id=self.event_id, chatroom=chatroom) + form.populate_obj(event_chatroom, fields=form.event_specific_fields) + form.populate_obj(chatroom, skip=form.event_specific_fields) + db.session.add_all((chatroom, event_chatroom)) + create_room(chatroom) + flash('Chatroom created', 'success') + return redirect(url_for_plugin('.manage_rooms', self.event)) + return WPChatEventMgmt.render_template('manage_event_edit.html', self._conf, form=form) + + +class RHChatManageEventAttach(RHChatManageEventBase): + """Attaches an existing chatroom to an event""" + + def _checkParams(self, params): + RHChatManageEventBase._checkParams(self, params) + self.chatroom = Chatroom.find_one(id=request.form['chatroom_id']) + + def _process(self): + event_chatroom = ChatroomEventAssociation(event_id=self.event_id, chatroom=self.chatroom) + db.session.add(event_chatroom) + flash('Chatroom added', 'success') + return redirect(url_for_plugin('.manage_rooms', self.event)) + + +class RHChatManageEventRemove(RHChatManageEventBase): + """Removes a chatroom from an event (and if necessary from the server)""" + + def _checkParams(self, params): + RHChatManageEventBase._checkParams(self, params) + self.event_chatroom = ChatroomEventAssociation.find_one(event_id=self.event_id, + chatroom_id=request.view_args['chatroom_id']) + self.chatroom = self.event_chatroom.chatroom + + def _process(self): + db.session.delete(self.event_chatroom) + db.session.flush() + if self.chatroom.events: + flash('Chatroom removed from event', 'success') + else: + db.session.delete(self.chatroom) + delete_room(self.chatroom) + flash('Chatroom deleted', 'success') + return redirect(url_for_plugin('.manage_rooms', self.event)) diff --git a/chat/indico_chat/forms.py b/chat/indico_chat/forms.py new file mode 100644 index 0000000..6430e6a --- /dev/null +++ b/chat/indico_chat/forms.py @@ -0,0 +1,42 @@ +# This file is part of Indico. +# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN). +# +# Indico is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# Indico 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 Indico; if not, see . + +from __future__ import unicode_literals + +from wtforms.fields.core import BooleanField +from wtforms.fields.simple import TextField, TextAreaField +from wtforms.validators import DataRequired + +from indico.web.forms.base import IndicoForm +from indico.util.string import strip_whitespace + + +class EditChatroomForm(IndicoForm): + event_specific_fields = {'hidden', 'show_password'} + + # Room-wide options + name = TextField('Name', [DataRequired()], filters=[strip_whitespace], description='The name of the room') + description = TextAreaField('Description', filters=[strip_whitespace], description='The description of the room') + password = TextField('Password', filters=[strip_whitespace], + description='An optional password required to join the room') + # Event-specific options + hidden = BooleanField('Hidden', description='Hides the room on public event pages.') + show_password = BooleanField('Show password', description='Shows the room password on public event pages.') + + +class AddChatroomForm(EditChatroomForm): + custom_server = TextField('Server', filters=[strip_whitespace], + description='External Jabber server. Should be left empty in most cases.') diff --git a/chat/indico_chat/models/chatrooms.py b/chat/indico_chat/models/chatrooms.py index fb62f4d..a2c9973 100644 --- a/chat/indico_chat/models/chatrooms.py +++ b/chat/indico_chat/models/chatrooms.py @@ -39,6 +39,7 @@ class Chatroom(db.Model): db.Integer, primary_key=True ) + # TODO: jid column #: Name of the chatroom name = db.Column( db.String, @@ -79,6 +80,10 @@ class Chatroom(db.Model): UTCDateTime ) + @property + def locator(self): + return {'chatroom_id': self.id} + @property def created_by_user(self): """The Avatar who created the chatroom.""" @@ -143,6 +148,10 @@ class ChatroomEventAssociation(db.Model): backref='events' ) + @property + def locator(self): + return dict(self.event.getLocator(), **self.chatroom.locator) + @property def event(self): return ConferenceHolder().getById(str(self.event_id)) diff --git a/chat/indico_chat/plugin.py b/chat/indico_chat/plugin.py index 2b3ebf9..1b3d8e0 100644 --- a/chat/indico_chat/plugin.py +++ b/chat/indico_chat/plugin.py @@ -23,16 +23,17 @@ from wtforms.fields.simple import TextField, TextAreaField from wtforms.validators import DataRequired from indico.core import signals -from indico.core.plugins import IndicoPlugin +from indico.core.plugins import IndicoPlugin, url_for_plugin from indico.web.forms.base import IndicoForm from indico.web.forms.fields import PrincipalField, MultipleItemsField, EmailListField, UnsafePasswordField from indico.web.forms.widgets import CKEditorWidget from MaKaC.webinterface.displayMgr import EventMenuEntry from MaKaC.webinterface.pages.conferences import WPTPLConferenceDisplay, WPXSLConferenceDisplay +from MaKaC.webinterface.wcomponents import SideMenuItem from indico_chat.blueprint import blueprint from indico_chat.models.chatrooms import ChatroomEventAssociation -from indico_chat.views import WPChatEventPage +from indico_chat.views import WPChatEventPage, WPChatEventMgmt class SettingsForm(IndicoForm): @@ -83,8 +84,9 @@ class ChatPlugin(IndicoPlugin): def init(self): super(ChatPlugin, self).init() self.connect(signals.event_sidemenu, self.extend_event_menu) + self.connect(signals.event_management_sidemenu, self.extend_event_management_menu) self.template_hook('event-header', self.inject_event_header) - for wp in (WPTPLConferenceDisplay, WPXSLConferenceDisplay, WPChatEventPage): + for wp in (WPTPLConferenceDisplay, WPXSLConferenceDisplay, WPChatEventPage, WPChatEventMgmt): self.inject_css('chat_css', wp) self.inject_js('chat_js', wp) @@ -109,5 +111,8 @@ class ChatPlugin(IndicoPlugin): ~ChatroomEventAssociation.hidden).count()) def extend_event_menu(self, sender, **kwargs): - return EventMenuEntry('chat.event-page', 'Chat Rooms', name='chat-event-page', plugin=True, + return EventMenuEntry('chat.event_page', 'Chat Rooms', name='chat-event-page', plugin=True, visible=self._has_visible_chatrooms) + + def extend_event_management_menu(self, event, **kwargs): + return 'chat-management', SideMenuItem('Chat Rooms', url_for_plugin('chat.manage_rooms', event)) diff --git a/chat/indico_chat/static/css/chat.scss b/chat/indico_chat/static/css/chat.scss index a66076b..00f46a3 100644 --- a/chat/indico_chat/static/css/chat.scss +++ b/chat/indico_chat/static/css/chat.scss @@ -13,7 +13,7 @@ dl.chat-details { clear: left; font-size: 90%; - margin: 5px 0 0 0; + margin: 5px 0 0 10px; dt { float: left; @@ -25,4 +25,19 @@ display: block; } } + + // Management + &.chat-mgmt { + .chat-custom-server { + font-weight: bold; + } + dl.chat-details { + dd { + margin-left: 150px; + } + } + .chatroom + .chatroom { + margin-top: 1em; + } + } } diff --git a/chat/indico_chat/static/js/chat.js b/chat/indico_chat/static/js/chat.js index 36f5ac1..bd28b2f 100644 --- a/chat/indico_chat/static/js/chat.js +++ b/chat/indico_chat/static/js/chat.js @@ -25,7 +25,7 @@ $('.chat-toggle-details').on('click', function(e) { e.preventDefault(); - $(this).siblings('.js-chat-details').slideToggle(); + $(this).siblings('.chat-details').slideToggle(); }); $('.js-chat-join').on('click', function(e) { @@ -50,5 +50,26 @@ var pos = $this.offset(); menu.open(pos.left - 3, pos.top + $this.height()); }); - } + }; + + global.eventManageChat = function eventManageChat() { + $('.js-chat-remove-room').on('click', function(e) { + e.preventDefault(); + var $this = $(this); + var msg = $T('Do you really want to delete this chatroom?'); + if ($this.data('numEvents') == 1) { + msg += '
' + $T('Since it is only used in this event, it will be deleted from the Jabber server, too!'); + } + new ConfirmPopup($T('Delete this chatroom?'), msg, function(confirmed) { + if (!confirmed) { + return; + } + + $('
', { + action: $this.data('href'), + method: 'post' + }).appendTo('body').submit(); + }).open(); + }); + }; })(window); diff --git a/chat/indico_chat/templates/event_header.html b/chat/indico_chat/templates/event_header.html index bafa120..74d98b5 100644 --- a/chat/indico_chat/templates/event_header.html +++ b/chat/indico_chat/templates/event_header.html @@ -7,13 +7,13 @@ {% set server = chatroom.server %}
{{ chatroom.name }} - + - {% trans %}More Info{% endtrans %} {% if chat_links %} - | + - {% trans %}Join now!{% endtrans %} {% endif %} -