def follow(context, request): """ /people/{username}/follows/{followedUsername}' """ #XXX TODO ara nomes es tracta un sol follow # s'ha de iterar si es vol que el comentari sigui de N follows rest_params = { 'actor': request.actor, 'verb': 'follow', 'object': { 'username': request.matchdict['followedUsername'], 'objectType': 'person' } } # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) code = 201 newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid request.actor.addFollower(newactivity['object']) handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def unfavorite(activity, request): """ Unfavorite activity """ # Prepare rest parameters to be merged with post data rest_params = { 'verb': 'unfavorite', 'object': { '_id': ObjectId(activity['_id']), 'objectType': activity['objectType'], } } # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid activity.delete_favorite_from(request.actor) newactivity['object']['favorites'] = activity['favorites'] newactivity['object']['favoritesCount'] = activity['favoritesCount'] newactivity['object']['favorited'] = activity.has_favorite_from(request.actor) handler = JSONResourceEntity(request, newactivity.flatten(), status_code=200) return handler.buildResponse()
def transferConversationOwnership(conversation, request): """ Transfer conversation ownership """ cid = request.matchdict.get('id', None) subscription = conversation.subscription # Check if the targeted new owner is on the conversation request.actor.getSubscription({'id': cid, 'objectType': 'conversation'}) previous_owner_username = conversation['_owner'] conversation['_owner'] = request.actor['username'] conversation.save() # Give hability to add new users to the new owner request.actor.grantPermission(subscription, 'invite', permanent=True) request.actor.grantPermission(subscription, 'kick', permanent=True) request.actor.revokePermission(subscription, 'unsubscribe', permanent=True) # Revoke hability to add new users from the previous owner users = MADMaxCollection(request, 'users', query_key='username') previous_owner = users[previous_owner_username] previous_owner.revokePermission(subscription, 'invite') previous_owner.revokePermission(subscription, 'kick') previous_owner.grantPermission(subscription, 'unsubscribe') handler = JSONResourceEntity(request, conversation.flatten()) return handler.buildResponse()
def subscribe(context, request): """ Subscribe user to context """ actor = request.actor rest_params = {'object': context, 'verb': 'subscribe'} # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) # Check if user is already subscribed subscribed_contexts_hashes = [a['hash'] for a in actor['subscribedTo']] if newactivity['object'].getHash() in subscribed_contexts_hashes: # If user already subscribed, send a 200 code and retrieve the original subscribe activity # post when user was subscribed. This way in th return data we'll have the date of subscription code = 200 activities = MADMaxCollection(request, 'activity') query = {'verb': 'subscribe', 'object.url': newactivity['object']['url'], 'actor.username': actor['username']} newactivity = activities.last(query) # Pick the last one, so we get the last time user subscribed (in cas a unsbuscription occured sometime...) else: actor.addSubscription(context) # If user wasn't created, 201 will show that the subscription has just been added code = 201 newactivity_oid = newactivity.insert() # Insert a subscribe activity newactivity['_id'] = newactivity_oid handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def ModifyUser(user, request): """ Modify a user Updates user information stored on the user object. Only known properties on request can be updated, and so any unknown property will be discarded silently. Any existing property value will be overriden by the new value. You also can unset a property by setting its value to `null. + Request { "displayName": "user2", "twitterUsername": "******" } > Note that properties other than the ones defined on the user creation method, that may be visible on the user profile (like `subscribedTo` or `talkingIn`) are not updatable using this endpoint. You must use the appropiate available methods for that goal. """ properties = user.getMutablePropertiesFromRequest(request) user.modifyUser(properties) user.updateConversationParticipants() handler = JSONResourceEntity(request, user.flatten()) return handler.buildResponse()
def like(activity, request): """ Like activity """ if activity.has_like_from(request.actor): code = 200 activities = MADMaxCollection(request, 'activity') query = {'verb': 'like', 'object._id': activity['_id'], 'actor.username': request.actor['username']} newactivity = activities.last(query) # Pick the last one, so we get the last time user liked this activity else: code = 201 # Prepare rest parameters to be merged with post data rest_params = { 'verb': 'like', 'object': { '_id': ObjectId(activity['_id']), 'objectType': activity['objectType'], } } # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid activity.add_like_from(request.actor) newactivity['object']['likes'] = activity['likes'] # Return the current likes of the activity newactivity['object']['likesCount'] = activity['likesCount'] # Return the current likes of the activity newactivity['object']['liked'] = activity.has_like_from(request.actor) handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def grantPermissionOnContext(context, request): """ Grant user permission on context """ permission = request.matchdict.get('permission', None) if permission not in DEFAULT_CONTEXT_PERMISSIONS.keys(): raise InvalidPermission("There's not any permission named '%s'" % permission) subscription = context.subscription if subscription is None: raise ObjectNotFound('{} is not susbcribed to {}'.format(request.actor, context['hash'])) if permission in subscription.get('_grants', []): # Already have the permission grant code = 200 else: # Assign the permission code = 201 subscription = request.actor.grantPermission( subscription, permission, permanent=request.params.get('permanent', DEFAULT_CONTEXT_PERMISSIONS_PERMANENCY)) handler = JSONResourceEntity(request, subscription, status_code=code) return handler.buildResponse()
def revokePermissionOnContext(context, request): """ Revoke user permission on context """ permission = request.matchdict.get('permission', None) if permission not in DEFAULT_CONTEXT_PERMISSIONS.keys(): raise InvalidPermission("There's not any permission named '%s'" % permission) subscription = context.subscription if subscription is None: raise ObjectNotFound('{} is not susbcribed to {}'.format(request.actor, context['hash'])) code = 200 if permission in subscription.get('_vetos', []): code = 200 # Alredy vetted else: # We have the permission, let's delete it subscription = request.actor.revokePermission( subscription, permission, permanent=request.params.get('permanent', DEFAULT_CONTEXT_PERMISSIONS_PERMANENCY)) code = 201 handler = JSONResourceEntity(request, subscription, status_code=code) return handler.buildResponse()
def getActivity(activity, request): """ Get an activity :rest activity The id of the activity """ handler = JSONResourceEntity(request, activity.flatten()) return handler.buildResponse()
def resetPermissionsOnContext(context, request): """ Reset user permissions on context """ subscription = request.actor.reset_permissions(context.subscription, context) handler = JSONResourceEntity(request, subscription, status_code=200) return handler.buildResponse()
def getUser(user, request): """ Get a user """ actor_info = user.getInfo() handler = JSONResourceEntity(request, actor_info) return handler.buildResponse()
def addContextActivity(context, request): """ Add a context activity If an actor is found on the request body it will be taken as the ownership of the activity, either the actor being a Person or a Context. If no actor specified on json payload, the current authenticated user will be taken as request.actor. """ rest_params = { 'verb': 'post', 'contexts': [ context ] } # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) # Search if there's any activity from the same user with # the same actor in the last minute actor_id_key = 'actor.{}'.format(request.actor.unique) actor_id_value = request.actor.get(request.actor.unique) query = { actor_id_key: actor_id_value, 'published': {'$gt': newactivity['published'] - timedelta(minutes=1)}, 'contexts.hash': context['hash'], 'verb': 'post' } possible_duplicates = request.db.activity.search(query) duplicated = False for candidate in possible_duplicates: if candidate['object']['content'] == newactivity['object'].get('content', ''): duplicated = candidate break if duplicated: code = 200 newactivity = duplicated else: # New activity code = 201 if newactivity['object']['objectType'] == u'image' or \ newactivity['object']['objectType'] == u'file': # Extract the file before saving object activity_file = newactivity.extract_file_from_activity() activity_oid = newactivity.insert() newactivity['_id'] = ObjectId(activity_oid) newactivity.process_file(request, activity_file) newactivity.save() else: activity_oid = newactivity.insert() newactivity['_id'] = ObjectId(activity_oid) handler = JSONResourceEntity(request, newactivity.flatten(squash=['keywords']), status_code=code) return handler.buildResponse()
def ModifyContext(context, request): """ Modify a context """ properties = context.getMutablePropertiesFromRequest(request) context.modifyContext(properties) context.updateUsersSubscriptions() context.updateContextActivities() handler = JSONResourceEntity(request, context.flatten()) return handler.buildResponse()
def add_message(conversation, request): """ Adds a message to a conversation The request.actor is the one "talking", either if it was the authenticated user, the rest username or the post body actor, in this order. """ try: mobile = request.decoded_payload['object']['mobile'] except: mobile = False message_params = {'actor': request.actor, 'verb': 'post', 'contexts': [conversation] } if 'single' in message_params['contexts'][0]['tags']: users = MADMaxCollection(request, 'users', query_key='username') for participant in message_params['contexts'][0]['participants']: user = users[participant['username']] if user.getSubscription(conversation) is None: user.addSubscription(conversation) conversation['tags'].remove('single') conversation.save() notifier = RabbitNotifications(request) notifier.add_conversation(conversation) # Initialize a Message (Activity) object from the request newmessage = Message.from_request(request, rest_params=message_params) if newmessage['object']['objectType'] == u'image' or \ newmessage['object']['objectType'] == u'file': # Extract the file before saving object message_file = newmessage.extract_file_from_activity() message_oid = newmessage.insert() newmessage['_id'] = ObjectId(message_oid) newmessage.process_file(request, message_file) newmessage.save() if mobile: notifier = RabbitNotifications(request) notifier.add_conversation_message(conversation, newmessage) else: message_oid = newmessage.insert() newmessage['_id'] = message_oid if mobile: notifier = RabbitNotifications(request) notifier.add_conversation_message(conversation, newmessage) handler = JSONResourceEntity(request, newmessage.flatten(), status_code=201) return handler.buildResponse()
def addUserActivity(user, request): """ Add a timeline activity Add activity posted as {username}. User in url will be taken as the actor that will own the activity. When url {username} and authenticated user don't match, user must have special permissions to be able to impersoate the activity. """ rest_params = {'actor': request.actor, 'verb': 'post'} # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) # Search if there's any activity from the same user with # the same actor and without context query = { 'actor.username': request.actor['username'], 'published': {'$gt': newactivity['published'] - timedelta(minutes=1)}, 'contexts': {'$exists': False}, 'verb': 'post' } possible_duplicates = request.db.activity.search(query) duplicated = None for candidate in possible_duplicates: if candidate['object']['content'] == newactivity['object'].get('content', ''): duplicated = candidate break if duplicated: code = 200 newactivity = duplicated else: # New activity code = 201 if newactivity['object']['objectType'] == u'image' or \ newactivity['object']['objectType'] == u'file': # Extract the file before saving object activity_file = newactivity.extract_file_from_activity() activity_oid = newactivity.insert() newactivity['_id'] = ObjectId(activity_oid) newactivity.process_file(request, activity_file) newactivity.save() else: activity_oid = newactivity.insert() newactivity['_id'] = activity_oid handler = JSONResourceEntity(request, newactivity.flatten(squash=['keywords']), status_code=code) return handler.buildResponse()
def joinConversation(conversation, request): """ Join conversation """ actor = request.actor cid = request.matchdict['id'] # Check if user is already subscribed if conversation.subscription: # If user already subscribed, send a 200 code and retrieve the original subscribe activity # post when user was subscribed. This way in th return data we'll have the date of subscription code = 200 activities = MADMaxCollection(request, 'activity') query = {'verb': 'subscribe', 'object.id': cid, 'actor.username': actor['username']} newactivity = activities.last(query) # Pick the last one, so we get the last time user subscribed (in cas a unsbuscription occured sometime...) else: if len(conversation['participants']) == CONVERSATION_PARTICIPANTS_LIMIT: raise Forbidden('This conversation is full, no more of {} participants allowed'.format(CONVERSATION_PARTICIPANTS_LIMIT)) if 'group' not in conversation.get('tags', []): raise Forbidden('This is not a group conversation, so no one else is allowed'.format(CONVERSATION_PARTICIPANTS_LIMIT)) if not request.creator.is_allowed_to_see(actor): raise Forbidden('User {} is not allowed to have a conversation with {}'.format(request.creator['username'], actor['username'])) conversation['participants'].append(actor.flatten(preserve=['displayName', 'objectType', 'username'])) actor.addSubscription(conversation) # If we add anyone to a conversation, we remove the archive tag, no matter how many participants have if 'archive' in conversation.get('tags', []): conversation['tags'].remove('archive') conversation.save() # If user wasn't created, 201 will show that the subscription has just been added code = 201 # Initialize a Activity object from the request rest_params = {'actor': actor, 'verb': 'subscribe', 'object': {'objectType': 'conversation', 'id': cid, 'participants': conversation['participants']} } newactivity = Activity.from_request(request, rest_params=rest_params) newactivity_oid = newactivity.insert() # Insert a subscribe activity newactivity['_id'] = newactivity_oid handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def flagActivity(activity, request): """ Flag an activity """ status_code = 200 # Flag only if not already flagged if activity.get('flagged', None) is None: activity.flag() activity.save() status_code = 201 handler = JSONResourceEntity(request, activity.flatten(), status_code=status_code) return handler.buildResponse()
def check_user_role(security, request): """ Check if user has a role """ role = request.matchdict['role'] user = request.matchdict['user'] if role not in ALLOWED_ROLES: raise ValidationError('Role "{}" is not a valid role'.format(role)) if not security.has_role(user, role): raise ObjectNotFound("User {} doesn't have role {}".format(user, role)) handler = JSONResourceEntity(request, {'roles': [role]}) return handler.buildResponse()
def get_deployment(deployment, request): """ Gets an existing deployment. """ deployment = deployment.as_dict() response = JSONResourceEntity(request, deployment, status_code=200) return response()
def add_component(deployment, request): """ Add a component to an existing deployment A new component of type specified in "component" field will be added to the deployment. If the parent field is given, an existing component will be searched and the new component will be added as a child. Type of the parent component will be determined by the aggregable attribute of the component type we're adding, so if no component named as requested and with the correct type is found, the component won't be added. """ component_type = request.json['component'] name = request.json['name'] title = request.json['title'] params = request.json['params'] parent = request.json.get('parent', None) if parent: component_spec = get_component(component_type) if component_spec.aggregable: parent_component = deployment.get_component(component_spec.aggregable.type, name=parent) else: parent_component = None component = deployment.add_component(component_type, name, title, params, parent_component=parent_component) response = JSONResourceEntity(request, component.as_dict(), status_code=201) return response()
def getUserConversationSubscription(conversation, request): """ Get a user conversation subscription """ subscription = conversation.subscription conversations_collection = conversation.__parent__ conversation_object = conversations_collection[subscription['id']] conversation = conversation_object.flatten() # Update temporary conversation with subscription permissions and other stuff conversation['displayName'] = conversation_object.realDisplayName(request.actor['username']) conversation['lastMessage'] = conversation_object.lastMessage() conversation['permissions'] = subscription['permissions'] conversation['messages'] = 0 handler = JSONResourceEntity(request, conversation) return handler.buildResponse()
def domain_add(domains, request): name = request.json['name'] title = request.json['title'] new_domain = domains.add(name, title) response = JSONResourceEntity(request, new_domain.as_dict(), status_code=201) return response()
def user_add(users, request): username = request.json['username'] domain = request.json['domain'] roles = request.json.get('roles', []) user = users.add(username, domain, roles) response = JSONResourceEntity(request, user.as_dict(), status_code=201) return response()
def add_device_token(tokens, request): """ Adds a user device token Adds a new user device linked to a user. If the token already exists for any user, we'll assume that the new user is using the old user's device, so we'll delete all the previous tokens and replace them with the new one. """ newtoken = Token.from_request(request) if '_id' in newtoken: newtoken.delete() newtoken = Token.from_request(request) # insert the token always newtoken.insert() handler = JSONResourceEntity(request, newtoken.flatten(), status_code=201) return handler.buildResponse()
def user_add_role(user, request): role = request.matchdict['role'] if role not in user.roles: user.roles.append(role) status = 201 else: status = 200 response = JSONResourceEntity(request, user.as_dict(), status_code=status) return response()
def domain_assign_component(domain, request): component_id = request.json['component_id'] deployment_name, component_type, component_name = re.match( r'^\s*(.*?)/(.*?):(.*?)\s*$', component_id).groups() deployments = root_factory(request)['deployments'] component = deployments['test'].get_component(component_type, name=component_name) domain.assign(component) response = JSONResourceEntity(request, domain.as_dict(), status_code=201) return response()
def domains_list(domains, request): info = { "default_maxserver_url": domains.default_maxserver_url, "domains": {} } for name, domain in domains.items(): info['domains'][name] = {"max_server_url": domain.max_server} response = JSONResourceEntity(request, info) return response()
def addActivityComment(activity, request): """ Add a comment to an activity """ # Prepare rest parameters to be merged with post data rest_params = { 'verb': 'comment', 'object': { 'inReplyTo': [{ '_id': activity['_id'], 'objectType': activity['object']['objectType'], 'contexts': [] }] } } # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) refering_activity_contexts = activity.get('contexts', []) if len(refering_activity_contexts) > 0: context_hashes = [ctxt['hash'] for ctxt in refering_activity_contexts] newactivity['object']['inReplyTo'][0]['contexts'] = context_hashes code = 201 newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid comment = dict(newactivity['object']) comment['published'] = newactivity['published'] comment['actor'] = request.actor comment['id'] = newactivity['_id'] del comment['inReplyTo'] activity.addComment(comment) handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def add_device_token(tokens, request): """ Adds a user device token Adds a new user device linked to a user. If the token already exists for any user, we'll assume that the new user is using the old user's device, so we'll delete all the previous tokens and replace them with the new one. 16/07/2018 Si este WS nos devuelve un 404 y no entra, es que no tenemos en el nginx del max el /tokens la nueva APP Utalk lo utiliza la antigua utiliza el fix_deprecated_add_token POST /people/{username}/device/{platform}/{token} """ newtoken = Token.from_request(request) if '_id' in newtoken: newtoken.delete() newtoken = Token.from_request(request) # insert the token always newtoken.insert() handler = JSONResourceEntity(request, newtoken.flatten(), status_code=201) return handler.buildResponse()
def addContext(contexts, request): """ Adds a context """ # Initialize a Context object from the request newcontext = Context.from_request(request) # If we have the _id setted, then the object already existed in the DB, # otherwise, proceed to insert it into the DB # In both cases, respond with the JSON of the object and the appropiate # HTTP Status Code if newcontext.get('_id'): # Already Exists code = 200 else: # New context code = 201 contextid = newcontext.insert() newcontext['_id'] = contextid handler = JSONResourceEntity(request, newcontext.flatten(), status_code=code) return handler.buildResponse()
def getException(context, request): """ Get an exception """ ehash = request.matchdict['hash'] exceptions_folder = request.registry.settings.get('exceptions_folder') matches = glob.glob('{}/*{}'.format(exceptions_folder, ehash)) if not matches: raise ObjectNotFound("There is no logged exception with this hash") exception = open(matches[0]).read() regex = r'BEGIN EXCEPTION REPORT: .*?\nDATE: (.*?)\nREQUEST:\n\n(.*?)\n\nTRACEBACK:\n\n(.*?)\nEND EXCEPTION REPORT' match = re.search(regex, exception, re.DOTALL) date, http_request, traceback = match.groups() result = { 'date': date, 'request': http_request, 'traceback': traceback } handler = JSONResourceEntity(request, result) return handler.buildResponse()
def getException(context, request): """ Get an exception """ ehash = request.matchdict['hash'] exceptions_folder = request.registry.settings.get('exceptions_folder') matches = glob.glob('{}/*{}'.format(exceptions_folder, ehash)) if not matches: raise ObjectNotFound("There is no logged exception with this hash") exception = open(matches[0]).read() regex = r'BEGIN EXCEPTION REPORT: .*?\nDATE: (.*?)\nREQUEST:\n\n(.*?)\n\nTRACEBACK:\n\n(.*?)\nEND EXCEPTION REPORT' match = re.search(regex, exception, re.DOTALL) date, http_request, traceback = match.groups() result = {'date': date, 'request': http_request, 'traceback': traceback} response = JSONResourceEntity(request, result) return response()
def add_deployment(deployments, request): """ Adds a new deployment. { "name": "deployment_id", "title": "Deployment Title" } """ params = request.json name = params['name'] title = params['title'] if params['name'] in deployments: status_code = 200 deployment = deployments[name] else: status_code = 201 deployment = deployments.add(name, title) response = JSONResourceEntity(request, deployment.as_dict(), status_code=status_code) return response()
def list_deployments(deployments, request): response = JSONResourceEntity(request, deployments.as_list(), status_code=200) return response()
def addUser(users, request): """ Add a user Creates a new user in the system, with all the attributes provided in the posted user object. - `username` - Used to identify and login the user. This is the only required parameter and cannot be modified. - `displayname` - The full name of the user. - `twitterUsername` - A valid Twitter® username (without @ prefix), used on the twitter integration service. This operation is idempotent, which means that a request to create a user that already exists, will not recreate or overwrite that user. Instead, the existing user object will be returned. You can tell when this happens by looking at the HTTP response status code. A new user insertion will return a **201 CREATED** code, and a **200 OK** for an existing user. + Request { "username": "******", "displayName": "user2", "twitterUsername": "******" } """ payload = request.decoded_payload if not isinstance(payload, dict): raise ValidationError('Unexpected data type in request') username = payload.get('username', None) if username is None: raise ValidationError('Missing username in request') rest_params = {'username': username.lower()} # Initialize a User object from the request newuser = User.from_request(request, rest_params=rest_params) # If we have the _id setted, then the object already existed in the DB, # otherwise, proceed to insert it into the DB # In both cases, respond with the JSON of the object and the appropiate # HTTP Status Code if newuser.get('_id'): # Already Exists code = 200 # Determine if we have to recreate exchanges for an existing user # Defaults NOT to recreate them if not specified create_exchanges = asbool(request.params.get('notifications', False)) if create_exchanges: notifier = RabbitNotifications(request) notifier.add_user(username) else: # New User code = 201 # Determine if we have to recreate exchanges for a new user # Defaults to Create them if not specified create_exchanges = asbool(request.params.get('notifications', True)) userid = newuser.insert(notifications=create_exchanges) newuser['_id'] = userid handler = JSONResourceEntity(request, newuser.getInfo(), status_code=code) return handler.buildResponse()
def user_remove_role(user, request): role = request.matchdict['role'] user.roles.remove(role) response = JSONResourceEntity(request, user.as_dict(), status_code=204) return response()
def domain(domain, request): response = JSONResourceEntity(request, domain.as_dict()) return response()
def getContext(context, request): """ Get a context """ handler = JSONResourceEntity(request, context.getInfo()) return handler.buildResponse()
def getConversation(conversation, request): """ Get a conversation """ handler = JSONResourceEntity(request, conversation.getInfo(request.actor['username'])) return handler.buildResponse()
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()