diff --git a/chat/indico_chat/migrations/201409291524_1bd6c5129d29_create_tables.py b/chat/indico_chat/migrations/201409291524_1bd6c5129d29_create_tables.py
new file mode 100644
index 0000000..eb221c5
--- /dev/null
+++ b/chat/indico_chat/migrations/201409291524_1bd6c5129d29_create_tables.py
@@ -0,0 +1,57 @@
+"""Create tables
+
+Revision ID: 1bd6c5129d29
+Revises: None
+Create Date: 2014-09-29 15:24:03.369025
+"""
+
+import sqlalchemy as sa
+from alembic import op
+from sqlalchemy.sql.ddl import CreateSchema, DropSchema
+
+from indico.core.db.sqlalchemy import UTCDateTime
+
+
+# revision identifiers, used by Alembic.
+revision = '1bd6c5129d29'
+down_revision = None
+
+
+def upgrade():
+ op.execute(CreateSchema('plugin_chat'))
+ op.create_table('chatrooms',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('name', sa.String(), nullable=False),
+ sa.Column('description', sa.Text(), nullable=False),
+ sa.Column('password', sa.String(), nullable=False),
+ sa.Column('custom_server', sa.String(), nullable=True),
+ sa.Column('created_by_id', sa.Integer(), nullable=False),
+ sa.Column('created_dt', UTCDateTime(), nullable=False),
+ sa.Column('modified_dt', UTCDateTime(), nullable=True),
+ sa.PrimaryKeyConstraint('id'),
+ schema='plugin_chat'
+ )
+ op.create_index('ix_chatrooms_name_lower', 'chatrooms', [sa.text('lower(plugin_chat.chatrooms.name)')], unique=True,
+ schema='plugin_chat')
+ op.create_table('chatroom_events',
+ sa.Column('event_id', sa.Integer(), nullable=False),
+ sa.Column('chatroom_id', sa.Integer(), nullable=False),
+ sa.Column('hidden', sa.Boolean(), nullable=False),
+ sa.Column('show_password', sa.Boolean(), nullable=False),
+ sa.ForeignKeyConstraint(['chatroom_id'], ['plugin_chat.chatrooms.id']),
+ sa.PrimaryKeyConstraint('event_id', 'chatroom_id'),
+ schema='plugin_chat')
+ op.create_index(op.f('ix_plugin_chat_chatroom_events_chatroom_id'), 'chatroom_events', ['chatroom_id'],
+ unique=False, schema='plugin_chat')
+ op.create_index(op.f('ix_plugin_chat_chatroom_events_event_id'), 'chatroom_events', ['event_id'], unique=False,
+ schema='plugin_chat')
+
+
+def downgrade():
+ op.drop_index(op.f('ix_plugin_chat_chatroom_events_event_id'), table_name='chatroom_events', schema='plugin_chat')
+ op.drop_index(op.f('ix_plugin_chat_chatroom_events_chatroom_id'), table_name='chatroom_events',
+ schema='plugin_chat')
+ op.drop_table('chatroom_events', schema='plugin_chat')
+ op.drop_index('ix_chatrooms_name_lower', table_name='chatrooms', schema='plugin_chat')
+ op.drop_table('chatrooms', schema='plugin_chat')
+ op.execute(DropSchema('plugin_chat'))
diff --git a/chat/indico_chat/models/__init__.py b/chat/indico_chat/models/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chat/indico_chat/models/chatrooms.py b/chat/indico_chat/models/chatrooms.py
new file mode 100644
index 0000000..2e49f58
--- /dev/null
+++ b/chat/indico_chat/models/chatrooms.py
@@ -0,0 +1,148 @@
+# 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 flask_pluginengine import current_plugin
+from sqlalchemy import func
+from sqlalchemy.ext.declarative import declared_attr
+
+from indico.core.db.sqlalchemy import db, UTCDateTime
+from indico.util.date_time import now_utc
+from indico.util.string import return_ascii
+from MaKaC.user import AvatarHolder
+from MaKaC.conference import ConferenceHolder
+
+
+class Chatroom(db.Model):
+ __tablename__ = 'chatrooms'
+
+ @declared_attr
+ def __table_args__(cls):
+ return (db.Index('ix_chatrooms_name_lower', func.lower(cls.name), unique=True),
+ {'schema': 'plugin_chat'})
+
+ #: Chatroom ID
+ id = db.Column(
+ db.Integer,
+ primary_key=True
+ )
+ #: Name of the chatroom
+ name = db.Column(
+ db.String,
+ nullable=False
+ )
+ #: Description of the chatroom
+ description = db.Column(
+ db.Text,
+ nullable=False,
+ default=''
+ )
+ #: Password to join the room
+ password = db.Column(
+ db.String,
+ nullable=False,
+ default=''
+ )
+ #: Custom Jabber MUC server hostname
+ custom_server = db.Column(
+ db.String
+ )
+ #: ID of the creator
+ created_by_id = db.Column(
+ db.Integer,
+ nullable=False
+ )
+ #: Creation timestamp of the chatroom
+ created_dt = db.Column(
+ UTCDateTime,
+ nullable=False,
+ default=now_utc
+ )
+ #: Modification timestamp of the chatroom
+ modified_dt = db.Column(
+ UTCDateTime
+ )
+
+ @property
+ def created_by_user(self):
+ """The Avatar who created the chatroom."""
+ return AvatarHolder().getById(str(self.created_by_id))
+
+ @created_by_user.setter
+ def created_by_user(self, user):
+ self.created_by_id = int(user.getId())
+
+ @property
+ def server(self):
+ """The server name of the chatroom.
+
+ Usually the default one unless a custom one is set.
+ """
+ return self.custom_server or current_plugin.settings.get('muc_server')
+
+ @return_ascii
+ def __repr__(self):
+ return ''.format(self.id, self.name, self.server)
+
+
+class ChatroomEventAssociation(db.Model):
+ __tablename__ = 'chatroom_events'
+ __table_args__ = {'schema': 'plugin_chat'}
+
+ #: ID of the event
+ event_id = db.Column(
+ db.Integer,
+ primary_key=True,
+ index=True
+ )
+ #: ID of the chatroom
+ chatroom_id = db.Column(
+ db.Integer,
+ db.ForeignKey('plugin_chat.chatrooms.id'),
+ primary_key=True,
+ index=True
+ )
+ #: If the chatroom should be hidden on the event page
+ hidden = db.Column(
+ db.Boolean,
+ nullable=False,
+ default=False
+ )
+ #: If the password should be visible on the event page
+ show_password = db.Column(
+ db.Boolean,
+ nullable=False,
+ default=False
+ )
+ #: The associated :class:Chatroom
+ chatroom = db.relationship(
+ 'Chatroom',
+ lazy=False,
+ backref='events'
+ )
+
+ @property
+ def event(self):
+ return ConferenceHolder().getById(str(self.event_id))
+
+ @event.setter
+ def event(self, event):
+ self.event_id = int(event.getId())
+
+ @return_ascii
+ def __repr__(self):
+ return ''.format(self.event_id, self.chatroom)
diff --git a/chat/indico_chat/plugin.py b/chat/indico_chat/plugin.py
index 313be3c..b08961a 100644
--- a/chat/indico_chat/plugin.py
+++ b/chat/indico_chat/plugin.py
@@ -14,7 +14,16 @@
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see .
+from __future__ import unicode_literals
+
from indico.core.plugins import IndicoPlugin
+from indico.web.forms.base import IndicoForm
+from wtforms.fields.simple import TextField
+
+
+class SettingsForm(IndicoForm):
+ server = TextField('XMPP server')
+ muc_server = TextField('XMPP MUC server (conference.*)')
class ChatPlugin(IndicoPlugin):
@@ -22,3 +31,5 @@ class ChatPlugin(IndicoPlugin):
Provides an XMPP based chat for events.
"""
+
+ settings_form = SettingsForm