diff --git a/zengine/management_commands.py b/zengine/management_commands.py index 746147e3..35e97b8e 100644 --- a/zengine/management_commands.py +++ b/zengine/management_commands.py @@ -192,19 +192,26 @@ class PrepareMQ(Command): Creates necessary exchanges, queues and bindings """ CMD_NAME = 'preparemq' - HELP = 'Creates necessary exchanges, queues and bindings' + HELP = 'Creates necessary exchanges, queues and bindings for messaging subsystem' def run(self): self.create_user_channels() self.create_channel_exchanges() def create_user_channels(self): - from zengine.messaging.model import Channel + from zengine.messaging.model import Channel, Subscriber user_model = get_object_from_path(settings.USER_MODEL) with BlockSave(Channel): for usr in user_model.objects.filter(): - ch, new = Channel.objects.get_or_create(owner=usr, is_private=True) + # create private exchange of user + ch, new = Channel.objects.get_or_create(owner=usr, typ=5) print("%s exchange: %s" % ('created' if new else 'existing', ch.code_name)) + # create notification subscription to private exchange + sb, new = Subscriber.objects.get_or_create(channel=ch, user=usr, is_visible=False, + can_leave=False, inform_me=False) + print("%s notify sub: %s" % ('created' if new else 'existing', ch.code_name)) + + def create_channel_exchanges(self): from zengine.messaging.model import Channel diff --git a/zengine/messaging/lib.py b/zengine/messaging/lib.py index a74009ff..ee43348b 100644 --- a/zengine/messaging/lib.py +++ b/zengine/messaging/lib.py @@ -16,6 +16,7 @@ from zengine.lib.cache import Cache from zengine.log import log + class ConnectionStatus(Cache): """ Cache object for workflow instances. @@ -70,11 +71,17 @@ def is_online(self, status=None): pass # TODO: do - def pre_save(self): + def encrypt_password(self): """ encrypt password if not already encrypted """ if self.password and not self.password.startswith('$pbkdf2'): self.set_password(self.password) + def prepare_channels(self): + from zengine.messaging.model import Channel, Subscriber + ch, new = Channel.objects.get_or_create(owner=self, typ=5) + sb, new = Subscriber.objects.get_or_create(channel=ch, user=self, is_visible=False, + can_leave=False, inform_me=False) + def check_password(self, raw_password): """ Verilen encrypt edilmemiş şifreyle kullanıcıya ait encrypt diff --git a/zengine/messaging/model.py b/zengine/messaging/model.py index cd3663b1..a83299c7 100644 --- a/zengine/messaging/model.py +++ b/zengine/messaging/model.py @@ -26,12 +26,16 @@ def get_mq_connection(): return connection, channel -# CHANNEL_TYPES = ( -# (1, "Notification"), -# (10, "System Broadcast"), -# (20, "Chat"), -# (25, "Direct"), -# ) +CHANNEL_TYPES = ( + # users private message hub + (5, "Private"), + # system notifications of user + # (10, "Notify"), + # a One-To-One communication between 2 user + (10, "Direct"), + # public chat rooms + (15, "Public"), +) class Channel(Model): @@ -47,24 +51,15 @@ class Channel(Model): mq_channel = None mq_connection = None + typ = field.Integer("Type", choices=CHANNEL_TYPES) name = field.String("Name") code_name = field.String("Internal name") description = field.String("Description") - owner = UserModel(reverse_name='created_channels') - # is this users private exchange - is_private = field.Boolean() - # is this a One-To-One channel - is_direct = field.Boolean() - - # typ = field.Integer("Type", choices=CHANNEL_TYPES) - - class Meta: - unique_together = (('is_private', 'owner'),) + owner = UserModel(reverse_name='created_channels', null=True) class Managers(ListNode): user = UserModel() - @classmethod def get_or_create_direct_channel(cls, initiator, receiver): """ @@ -90,8 +85,9 @@ def get_or_create_direct_channel(cls, initiator, receiver): return channel @classmethod - def add_message(self, channel_key, body, title=None, sender=None, url=None, typ=2, receiver=None): - mq_channel = self._connect_mq() + def add_message(cls, channel_key, body, title=None, sender=None, url=None, typ=2, + receiver=None): + mq_channel = cls._connect_mq() mq_msg = json.dumps(dict(sender=sender, body=body, msg_title=title, url=url, typ=typ)) mq_channel.basic_publish(exchange=channel_key, routing_key='', body=mq_msg) return Message(sender=sender, body=body, msg_title=title, url=url, @@ -117,6 +113,16 @@ def create_exchange(self): exchange_type='fanout', durable=True) + def get_actions_for(self, user): + actions = [ + ('Pin', 'pin_channel') + ] + if self.sender == user: + actions.extend([ + ('Delete', 'zops_delete_channel'), + ('Edit', 'zops_edit_channel') + ]) + def pre_creation(self): if not self.code_name: if self.name: @@ -142,11 +148,11 @@ class Subscriber(Model): channel = Channel() user = UserModel(reverse_name='subscriptions') - is_muted = field.Boolean("Mute the channel") + is_muted = field.Boolean("Mute the channel", default=False) inform_me = field.Boolean("Inform when I'm mentioned", default=True) - visible = field.Boolean("Show under user's channel list", default=True) + is_visible = field.Boolean("Show under user's channel list", default=True) can_leave = field.Boolean("Membership is not obligatory", default=True) - last_seen = field.DateTime("Last seen time") + last_seen_msg_time = field.DateTime("Last seen message's time") # status = field.Integer("Status", choices=SUBSCRIPTION_STATUS) @@ -158,7 +164,8 @@ def _connect_mq(cls): def unread_count(self): # FIXME: track and return actual unread message count - return 0 + return self.channel.message_set.objects.filter( + timestamp__lt=self.last_seen_msg_time).count() def create_exchange(self): """ @@ -236,8 +243,8 @@ def get_actions_for(self, user): ] if self.sender == user: actions.extend([ - ('Delete', 'delete_message'), - ('Edit', 'delete_message') + ('Delete', 'zops_delete_message'), + ('Edit', 'zops_edit_message') ]) else: actions.extend([ diff --git a/zengine/messaging/views.py b/zengine/messaging/views.py index 7d520304..6a3e1297 100644 --- a/zengine/messaging/views.py +++ b/zengine/messaging/views.py @@ -8,7 +8,7 @@ # (GPLv3). See LICENSE.txt for details. from pyoko.conf import settings from pyoko.lib.utils import get_object_from_path -from zengine.messaging.model import Channel, Attachment +from zengine.messaging.model import Channel, Attachment, Subscriber from zengine.views.base import BaseView UserModel = get_object_from_path(settings.USER_MODEL) @@ -67,7 +67,7 @@ def show_channel(current): # request: { - 'view':'_zops_show_public_channel', + 'view':'_zops_show_channel', 'channel_key': key, } @@ -108,7 +108,8 @@ def show_channel(current): for msg in ch.get_last_messages()] } -def last_seen_channel(current): + +def last_seen_msg(current): """ Initial display of channel content. Returns channel description, members, no of members, last 20 messages etc. @@ -124,40 +125,49 @@ def last_seen_channel(current): 'msg_date': datetime, } + # response: + None + """ + Subscriber.objects.filter(channel_id=current.input['channel_key'], + user_id=current.user_id + ).update(last_seen_msg_time=current.input['msg_date']) + + + + + +def list_channels(current): + """ + List channel memberships of current user + + + .. code-block:: python + + # request: + { + 'view':'_zops_list_channels', + } + # response: { - 'channel_key': key, - 'description': string, - 'no_of_members': int, - 'member_list': [ + 'channels': [ {'name': string, - 'is_online': bool, - 'avatar_url': string, - }], - 'last_messages': [ - {'content': string, - 'title': string, - 'channel_key': key, - 'sender_name': string, - 'sender_key': key, + 'key': key, + 'unread': int, 'type': int, 'key': key, 'actions':[('name_string', 'cmd_string'),] } ] } - """ - current.input['seen_channel'] - -def mark_offline_user(current): - current.user.is_online(False) - -def list_channels(current): - return [ + """ + current.output['channels'] = [ {'name': sbs.channel.name, 'key': sbs.channel.key, + 'type': sbs.channel.typ, + 'actions': sbs.channel.get_actions_for(current.user), 'unread': sbs.unread_count()} for sbs in - current.user.subscriptions] + current.user.subscriptions if sbs.is_visible] def create_public_channel(current): diff --git a/zengine/models/auth.py b/zengine/models/auth.py index deb3af65..986595da 100644 --- a/zengine/models/auth.py +++ b/zengine/models/auth.py @@ -56,6 +56,11 @@ class Meta: """ list_fields = ['username', 'superuser'] + def pre_save(self): + self.encrypt_password() + + def post_creation(self): + self.prepare_channels() def get_permissions(self): """ diff --git a/zengine/settings.py b/zengine/settings.py index ba94d4f3..b75f30d2 100644 --- a/zengine/settings.py +++ b/zengine/settings.py @@ -115,10 +115,11 @@ VIEW_URLS = { 'dashboard': 'zengine.views.menu.Menu', 'sessid_to_userid': 'zengine.views.system.sessid_to_userid', + 'mark_offline_user': 'zengine.views.system.mark_offline_user', 'ping': 'zengine.views.dev_utils.Ping', '_zops_create_message': 'zengine.messaging.views.create_message', - 'mark_offline_user': 'zengine.messaging.views.mark_offline_user', - 'show_channel': 'zengine.messaging.views.show_channel', + '_zops_show_channel': 'zengine.messaging.views.show_channel', + '_zops_list_channels': 'zengine.messaging.views.list_channels', } if DEBUG: diff --git a/zengine/views/system.py b/zengine/views/system.py index e2215f28..a592faf2 100644 --- a/zengine/views/system.py +++ b/zengine/views/system.py @@ -7,10 +7,12 @@ # This file is licensed under the GNU General Public License v3 # (GPLv3). See LICENSE.txt for details. -from zengine.lib.cache import UserSessionID def sessid_to_userid(current): current.output['user_id'] = current.user_id.lower() current.output['sess_id'] = current.session.sess_id current.user.bind_private_channel(current.session.sess_id) current.output['sessid_to_userid'] = True + +def mark_offline_user(current): + current.user.is_online(False)