def postMessage2Conversation(conversations, request): """ Add a new conversation """ # We are forced the check and extract the context of the conversation here, # We can't initialize the activity first, because it would fail (chiken-egg stuff) data = request.decoded_payload ctxts = data.get('contexts', []) if len(ctxts) == 0: raise ValidationError('Empty contexts parameter') request_participants = ctxts[0].get('participants', []) if len(request_participants) == 0: raise ValidationError('Empty participants parameter') if len(request_participants) != len(list(set(request_participants))): raise ValidationError('One or more users duplicated in participants list') if len(request_participants) == 1 and request_participants[0] == request.actor['username']: raise ValidationError('Cannot start a conversation with oneself') if request.actor['username'] not in request_participants and not request.has_permission(add_conversation_for_others): raise ValidationError('Actor must be part of the participants list.') # Loop trough all participants, if there's one that doesn't exists, an exception will raise # This check is to avoid any conversation creation if there's any invalid participant # Also store the definitive list that will be saved in participants field participants = {} users = MADMaxCollection(request, 'users', query_key='username') for participant in request_participants: user = users[participant] if request.actor['username'] != user['username'] and not request.actor.is_allowed_to_see(user): raise Forbidden('User {} is not allowed to have a conversation with {}'.format(request.actor['username'], user['username'])) participants[participant] = user # If there are only two participants in the conversation, try to get an existing conversation # Otherwise, assume is a group conversation and create a new one current_conversation = None if len(request_participants) == 2: current_conversation = conversations.first({ 'objectType': 'conversation', 'participants': { '$size': 2}, 'tags': {'$not': {'$in': ['group']}}, 'participants.username': { '$all': request_participants} }) if current_conversation and 'single' in current_conversation['tags']: for participant in participants: if participants[participant].getSubscription(current_conversation) is None: participants[participant].addSubscription(current_conversation) current_conversation['tags'].remove('single') current_conversation.save() if current_conversation is None: # Initialize a conversation (context) object from the request, overriding the object using the context conversation_params = dict(actor=request.actor, tags=['group'] if len(participants) > 2 else [], participants=[participant.flatten(preserve=['displayName', 'objectType', 'username']) for participant in participants.values()], permissions={'read': 'subscribed', 'write': 'subscribed', 'subscribe': 'restricted', 'unsubscribe': 'subscribed'}) if ctxts[0].get('displayName', False): conversation_params['displayName'] = ctxts[0]['displayName'] newconversation = Conversation.from_request(request, rest_params=conversation_params) # New conversation contextid = newconversation.insert() newconversation['_id'] = contextid # Subscribe everyone, for user in newconversation['participants']: db_user = participants[user['username']] db_user.addSubscription(newconversation) # Initialize a Subscription Activity rest_params = {'actor': db_user, 'verb': 'subscribe', 'object': {'objectType': 'conversation', 'id': newconversation['_id'], 'participants': newconversation['participants']}, 'contexts': [] # Override contexts from request } newactivity = Activity.from_request(request, rest_params=rest_params) newactivity_oid = newactivity.insert() # Insert a subscribe activity newactivity['_id'] = newactivity_oid current_conversation = newconversation # We need to reload the actor, in order to have the subscription updated # We need to reload reified acl's, so then new actor subscription will be visible by __acl__ request.actor.reload() current_conversation.reload__acl__() message_params = {'actor': request.actor, 'contexts': [current_conversation], 'verb': 'post'} try: # Initialize a Message (Activity) object from the request newmessage = Message.from_request(request, rest_params=message_params) except Exception as catched: # In case we coulnd't post the message, rollback conversation creation current_conversation.delete() raise catched # Grant subscribe permission to the user creating the conversation, only if the conversation # is bigger than 2 people. Conversations that are created with only 2 people from the beggining # Will not be able to grow if len(current_conversation['participants']) > 2: subscription = request.actor.getSubscription(current_conversation) request.actor.grantPermission(subscription, 'invite', permanent=False) request.actor.grantPermission(subscription, 'kick', permanent=False) request.actor.revokePermission(subscription, 'unsubscribe', permanent=False) message_oid = newmessage.insert() newmessage['_id'] = message_oid output_message = newmessage.flatten() output_message['contexts'][0]['displayName'] = current_conversation.realDisplayName(request.actor) output_message['contexts'][0]['tags'] = current_conversation.get('tags', []) # Notification is done here because we don't want to do it right after insertion # as a possible rollback would cause a notification of a inexistent conversation notifier = RabbitNotifications(request) notifier.add_conversation(current_conversation) handler = JSONResourceEntity(request, output_message, status_code=201) return handler.buildResponse()
def rebuildConversationSubscriptions(context, request): """ Rebuild conversation subscriptions Performs sanity checks on existing subscriptions """ existing_conversations = {} conversations = request.db.conversations.dump() for conversation in conversations: # if we found an ancient plain username list, we migrate it if True not in [isinstance(a, dict) for a in conversation['participants']]: conversation['participants'] = [{'username': a, 'displayName': a, 'objectType': 'person'} for a in conversation['participants']] conversation.save() conversation.updateUsersSubscriptions(force_update=True) conversation.updateContextActivities(force_update=True) existing_conversations[str(conversation['_id'])] = conversation subscribed_users_by_conversation = {} users = request.db.users.search({'talkingIn.0': {'$exists': True}}) for user in users: for subscription in user.get('talkingIn', []): if subscription['id'] not in existing_conversations: fake_deleted_conversation = Conversation.from_object(request, subscription) user.removeSubscription(fake_deleted_conversation) user['talkingIn'] = [a for a in user['talkingIn'] if a['id'] != subscription['id']] else: # if subscription has an ancient plain username list, update and save it if True not in [isinstance(a, dict) for a in subscription['participants']]: subscription['participants'] = existing_conversations[subscription['id']]['participants'] subscribed_users_by_conversation.setdefault(subscription['id'], []) subscribed_users_by_conversation[subscription['id']].append(user['username']) user.updateConversationParticipants(force_update=True) user.save() existing_users = request.db.users.search({}, show_fields={'username': True, '_id': False}) existing_users_set = set([a['username'] for a in existing_users]) conversations = request.db.conversations.dump() for conversation in conversations: conversation_participants_usernames = [user['username'] for user in conversation['participants']] conversation_subscribed_usernames = subscribed_users_by_conversation[str(conversation['_id'])] not_subscribed = set(conversation_participants_usernames) - set(conversation_subscribed_usernames) deleted_participants = set(not_subscribed) - existing_users_set all_participants_subscribed = len(conversation_participants_usernames) == len(conversation_subscribed_usernames) all_participants_exist = len(deleted_participants) == 0 if 'single' in conversation['tags']: conversation['tags'].remove('single') if 'archive' in conversation['tags']: conversation['tags'].remove('archive') # Conversations od 2+ get the group tag if len(conversation['participants']) > 2: if 'group' not in conversation['tags']: conversation['tags'].append('group') # Two people conversation and not group: # tag single: if not all participants subscribed by all exist # tag archive: if not all participants exist elif len(conversation['participants']) == 2 and 'group' not in conversation['tags']: if all_participants_subscribed: pass elif not all_participants_subscribed and all_participants_exist: conversation['tags'].append('single') elif not all_participants_subscribed and not all_participants_exist: conversation['tags'].append('archive') # Tag archive: if group conversation only 1 participant elif 'group' in conversation['tags'] and len(conversation['participants']) == 1: conversation['tags'].append('archive') conversation.save() handler = JSONResourceRoot(request, []) return handler.buildResponse()