Chatroom management (and some more stuff)

This commit is contained in:
Adrian Moennich 2014-10-07 10:43:51 +02:00
parent 4671d85df7
commit f650f4225e
12 changed files with 385 additions and 16 deletions

View File

@ -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/<confId>/chat-new', 'event-page', RHChatEventPage)
# TODO: s/chat-new/chat/
blueprint = IndicoPluginBlueprint('chat', 'indico_chat', url_prefix='/event/<confId>')
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/<int:chatroom_id>/', 'manage_rooms_modify', RHChatManageEventModify,
methods=('GET', 'POST'))
blueprint.add_url_rule('/manage/chat-new/<int:chatroom_id>/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',))

View File

@ -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))

42
chat/indico_chat/forms.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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.')

View File

@ -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))

View File

@ -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))

View File

@ -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;
}
}
}

View File

@ -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 += '<br>' + $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;
}
$('<form>', {
action: $this.data('href'),
method: 'post'
}).appendTo('body').submit();
}).open();
});
};
})(window);

View File

@ -7,13 +7,13 @@
{% set server = chatroom.server %}
<div class="chatroom">
<span>{{ chatroom.name }}</span>
<span style="margin-left: 20px;"></span>
-
<a class="chat-toggle-details" href="#">{% trans %}More Info{% endtrans %}</a>
{% if chat_links %}
<span style="margin-left: 8px; margin-right: 8px;">|</span>
-
<strong><a class="dropDownMenu highlight js-chat-join" href="#" data-server="{{ server }}" data-room="{{ chatroom.name }}">{% trans %}Join now!{% endtrans %}</a></strong>
{% endif %}
<dl class="js-chat-details chat-details" style="display: none;">
<dl class="chat-details" style="display: none;">
<dt>{% trans %}Name{% endtrans %}:</dt>
<dd>{{ chatroom.name }}</dd>
<dt>{% trans %}Server{% endtrans %}:</dt>

View File

@ -0,0 +1,71 @@
<h2 class="page-title">{% trans %}Chat Rooms{% endtrans %}</h2>
<div class="plugin-chat chat-mgmt" id="chat-info-container" data-chat-links="{{ chat_links | tojson | forceescape }}">
<div class="groupTitle">{% trans %}Current chat rooms{% endtrans %}</div>
{% for event_chatroom in event_chatrooms %}
{% set chatroom = event_chatroom.chatroom %}
{% set server = chatroom.server %}
<div class="chatroom">
<span><strong>{{ chatroom.name }}</strong></span>
-
<a class="chat-toggle-details" href="#">{% trans %}Show Details{% endtrans %}</a>
-
<a href="{{ url_for_plugin('.manage_rooms_modify', event_chatroom) }}">{% trans %}Edit{% endtrans %}</a>
-
<a class="js-chat-remove-room" href="#" data-href="{{ url_for_plugin('.manage_rooms_remove', event_chatroom) }}" data-num-events="{{ chatroom.events | count }}">{% trans %}Remove{% endtrans %}</a>
{% if chat_links %}
-
<strong><a class="dropDownMenu highlight js-chat-join" href="#" data-server="{{ server }}" data-room="{{ chatroom.name }}">{% trans %}Join now!{% endtrans %}</a></strong>
{% endif %}
<dl class="chat-details" style="display: none;">
<dt>{% trans %}Name{% endtrans %}:</dt>
<dd>{{ chatroom.name }}</dd>
<dt>{% trans %}Server{% endtrans %}:</dt>
<dd><span class="{% if chatroom.custom_server %}chat-custom-server{% endif %}">{{ server }}</span></dd>
{% if chatroom.description %}
<dt>{% trans %}Description{% endtrans %}:</dt>
<dd>{{ chatroom.description }}</dd>
{% endif %}
<dt>{% trans %}Hidden{% endtrans %}:</dt>
<dd>{% if event_chatroom.hidden %}Yes{% else %}No{% endif %}</dd>
{% if chatroom.password %}
<dt>{% trans %}Password{% endtrans %}:</dt>
<dd>{{ chatroom.password }}</dd>
<dt>{% trans %}Password visible{% endtrans %}:</dt>
<dd>{% if event_chatroom.show_password %}Yes{% else %}No{% endif %}</dd>
{% endif %}
<dt>Created by:</dt>
<dd>{{ chatroom.created_by_user.getStraightFullName() }}</dd>
<dt>Created on:</dt>
<dd>{{ chatroom.created_dt | format_datetime('long') }}</dd>
<dt>Modified on:</dt>
<dd>
{% if chatroom.modified_dt %}
{{ chatroom.modified_dt | format_datetime('long') }}
{% else %}
<em>Never</em>
{% endif %}
</dd>
</dl>
</div>
{% endfor %}
</div>
<div style="margin-top: 2em;">
<div class="groupTitle">{% trans %}Add chat room{% endtrans %}</div>
<a class="i-button" href="{{ url_for_plugin('.manage_rooms_create', event) }}">{% trans %}Create new room{% endtrans %}</a>
{% if available_chatrooms %}
<form action="{{ url_for_plugin('.manage_rooms_attach', event) }}" method="post">
<select name="chatroom_id">
{% for chatroom in available_chatrooms -%}
<option value="{{ chatroom.id }}">{{ chatroom.name }}</option>
{% endfor %}
</select>
<input class="i-button" type="submit" value="{% trans %}Attach{% endtrans %}">
</form>
{% endif %}
</div>
<script>
eventChatInfo();
eventManageChat();
</script>

View File

@ -0,0 +1,42 @@
<h2 class="page-title">{% trans %}Chat Rooms{% endtrans %}</h2>
{% set chatroom = event_chatroom.chatroom if event_chatroom %}
<div>
<div class="groupTitle">
{% if chatroom %}
{% trans %}Edit chatroom{% endtrans %}
{% else %}
{% trans %}Create chatroom{% endtrans %}
{% endif %}
</div>
{% if form.errors %}
<div class="error-message-box">
<div class="message-text">
{% trans %}Please correct the following errors:{% endtrans %}
<ul>
{% for error in form.error_list -%}
<li>{{ error }}</li>
{% endfor -%}
</ul>
</div>
</div>
{% endif %}
<form method="post" action="">
<table>
{% for field in form.visible_fields if field.short_name not in form.event_specific_fields %}
<tr>
<td style="text-align: right; vertical-align: top;">{{ field.label() }}</td>
<td>
{{ field() }}
{% if field.description %}
<p style="margin-top: 0.25em;"><em>{{ field.description }}</em></p>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
<input class="i-button" type="submit" value="{% trans %}Save{% endtrans %}">
</form>
</div>

View File

@ -17,7 +17,7 @@
from __future__ import unicode_literals
from indico.core.plugins import WPJinjaMixinPlugin
from MaKaC.webinterface.pages.conferences import WPConferenceDefaultDisplayBase
from MaKaC.webinterface.pages.conferences import WPConferenceDefaultDisplayBase, WPConferenceModifBase
class WPChatEventPage(WPJinjaMixinPlugin, WPConferenceDefaultDisplayBase):
@ -32,3 +32,8 @@ class WPChatEventPage(WPJinjaMixinPlugin, WPConferenceDefaultDisplayBase):
def _defineSectionMenu(self):
WPConferenceDefaultDisplayBase._defineSectionMenu(self)
self._sectionMenu.setCurrentItem(self._sectionMenu.getLinkByName('chat-event-page'))
class WPChatEventMgmt(WPJinjaMixinPlugin, WPConferenceModifBase):
def _setActiveSideMenuItem(self):
self._pluginMenuItems['chat-management'].setActive(True)

35
chat/indico_chat/xmpp.py Normal file
View File

@ -0,0 +1,35 @@
# 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 <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
# TODO: implement the XMPP gateway
def create_room(room):
"""Creates a `Chatroom` on the XMPP server."""
print 'create_room / not implemented yet'
def update_room(room):
"""Updates a `Chatroom` on the XMPP server."""
print 'update_room / not implemented yet'
def delete_room(room):
"""Deletes a `Chatroom` from the XMPP server."""
print 'delete_room / not implemented yet'