diff --git a/backend_modules/rooms/__init__.py b/backend_modules/rooms/__init__.py
new file mode 100644
index 0000000..b5ad6c5
--- /dev/null
+++ b/backend_modules/rooms/__init__.py
@@ -0,0 +1,39 @@
+# -*- coding: UTF-8 -*-
+
+# COPYRIGHT (c) 2016 Cristóbal Ganter
+#
+# GNU AFFERO GENERAL PUBLIC LICENSE
+# Version 3, 19 November 2007
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+from .wsclass import RoomsWSC # noqa
+from . import patches # noqa
+
+class RoomIsNotDefined(AttributeError):
+ """Raise when ``room`` is not defined.
+
+ ``course`` should be defined in the current instance of
+ :class:`~controller.MSGHandler`.
+
+ .. automethod:: __init__
+ """
+
+ def __init__(self, *args):
+ """Initialize a new CourseIsNotDefined exception."""
+ super().__init__(
+ 'Attempted to use the `course` attribute, but '
+ 'the attribute is not assigned.',
+ *args
+ )
diff --git a/backend_modules/rooms/patches.py b/backend_modules/rooms/patches.py
new file mode 100644
index 0000000..c47de36
--- /dev/null
+++ b/backend_modules/rooms/patches.py
@@ -0,0 +1,47 @@
+# -*- coding: UTF-8 -*-
+
+# COPYRIGHT (c) 2016 Cristóbal Ganter
+#
+# GNU AFFERO GENERAL PUBLIC LICENSE
+# Version 3, 19 November 2007
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+from controller import MSGHandler
+from src.db import Room
+from .. import router
+from .wsclass import RoomsWSC
+
+MSGHandler._room = None
+
+@property
+def room(self):
+ """Current room asociated with this MSGHandler."""
+ return self._room
+
+@room.setter
+def room(self, new_room):
+ self._room = new_room
+ self.room_msg_type = \
+ 'message.filter.room({})'.format(new_room.id)
+ router_object = self.ws_objects[
+ router.RouterWSC]
+ rooms_object = self.ws_objects[RoomsWSC]
+
+ rooms_object.register_action_in(
+ self.room_msg_type,
+ action=router_object.to_local,
+ channels={'d'}
+ )
+MSGHandler.room = room
diff --git a/backend_modules/rooms/wsclass.py b/backend_modules/rooms/wsclass.py
new file mode 100644
index 0000000..eae5531
--- /dev/null
+++ b/backend_modules/rooms/wsclass.py
@@ -0,0 +1,29 @@
+# -*- coding: UTF-8 -*-
+
+# COPYRIGHT (c) 2016 Cristóbal Ganter
+#
+# GNU AFFERO GENERAL PUBLIC LICENSE
+# Version 3, 19 November 2007
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+from tornado.gen import coroutine
+
+import src
+from src.wsclass import subscribe
+from src.db import Course, User
+
+
+class RoomsWSC(src.wsclass.WSClass):
+ pass
diff --git a/panels/user/__init__.py b/panels/user/__init__.py
index 537fb85..73ffb74 100644
--- a/panels/user/__init__.py
+++ b/panels/user/__init__.py
@@ -174,6 +174,52 @@ def load_user(self, token):
)
raise ite from norfdb
+ @subscribe('message.filter.student', 'l')
+ @coroutine
+ def redirect_message_if_user_is_student(
+ self, message, content=True):
+ """Redirects a message if the user is a student.
+
+ This coroutine redirects a message to the local
+ channel only if the current user is a student.
+
+ :param dict message:
+ The message that should be redirected if the
+ user is a student.
+
+ :param bool content:
+ If ``True``, just the object corresponding to
+ the ``'content'`` key of ``message`` will be
+ sent.
+ If ``False``, the whole message will be sent.
+
+ :raises MalformedMessageError:
+ If ``content`` is ``True``, but ``message``
+ doesn't have the ``'content'`` key.
+
+ :raises NotDictError:
+ If ``message`` is not a dictionary.
+
+ :raises NoMessageTypeError:
+ If the message or it's content doesn't have the
+ ``'type'`` key.
+
+ :raises NoActionForMsgTypeError:
+ If ``send_function`` of the ``PubSub`` object
+ wasn't specified during object creation and
+ there's no registered action for this message
+ type.
+
+ :raises AttributeError:
+ If the user is not yet loaded or if the user is
+ ``None``.
+ """
+ try:
+ if self.handler.user.status == 'seat':
+ self.redirect_to('l', message, content)
+ except:
+ raise
+
@subscribe('teacherMessage', 'l')
@coroutine
def redirect_message_if_user_is_teacher(