Exemple #1
0
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()
Exemple #2
0
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()