def addActivityComment(context, request): """ POST /activities/{activity}/comments """ #XXX TODO ara només es tracta la primera activitat, # s'ha de iterar si es vol que el comentari sigui de N activitats activityid = request.matchdict['activity'] mmdb = MADMaxDB(context.db) refering_activity = mmdb.activity[activityid] # Prepare rest parameters to be merged with post data rest_params = {'verb': 'comment', 'object': {'inReplyTo': [{'_id':ObjectId(activityid), 'objectType':refering_activity.object['objectType']}]}} # Initialize a Activity object from the request newactivity = Activity() newactivity.fromRequest(request, rest_params=rest_params) code = 201 newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid comment = dict(newactivity.object) comment['published'] = newactivity.published comment['author'] = request.actor comment['id'] = newactivity._id del comment['inReplyTo'] refering_activity.addComment(comment) handler = JSONResourceEntity(newactivity.flatten(), status_code=code) 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 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 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 addAdminUserActivity(context, request): """ /admin/people|contexts/{username|urlHash}/activities Add activity impersonated as a valid MAX user or context """ rest_params = {'actor': request.actor, 'verb': 'post'} # Initialize a Activity object from the request newactivity = Activity() newactivity.fromRequest(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 newactivity.get('_id'): # Already Exists code = 200 else: # New User code = 201 activity_oid = newactivity.insert() newactivity['_id'] = activity_oid handler = JSONResourceEntity(newactivity.flatten(), status_code=code) 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 follow(context, request): """ /people/{username}/follows/{followedDN}' """ #XXX TODO ara nomes es tracta un sol follow # s'ha de iterar si es vol que el comentari sigui de N follows actor = request.actor rest_params = {'actor': request.actor} # Initialize a Activity object from the request newactivity = Activity(request, rest_params=rest_params) code = 201 newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid actor.addFollower(newactivity['object']) handler = JSONResourceEntity(newactivity.flatten(), status_code=code) return handler.buildResponse()
def addUserActivity(context, request): """ /users/{username}/activities Afegeix una activitat """ rest_params = {'actor': request.actor, 'verb': 'post'} # Initialize a Activity object from the request newactivity = Activity() newactivity.fromRequest(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 newactivity.get('_id'): # Already Exists code = 200 else: # New User code = 201 activity_oid = newactivity.insert() newactivity['_id'] = activity_oid handler = JSONResourceEntity(newactivity.flatten(), status_code=code) 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 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 unlike(activity, request): """ Unlike activity """ # Prepare rest parameters to be merged with post data rest_params = { 'verb': 'unlike', '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_like_from(request.actor) return HTTPNoContent()
def addActivityComment(context, request): """ POST /activities/{activity}/comments """ #XXX TODO ara només es tracta la primera activitat, # s'ha de iterar si es vol que el comentari sigui de N activitats activityid = request.matchdict['activity'] mmdb = MADMaxDB(context.db) refering_activity = mmdb.activity[activityid] # Prepare rest parameters to be merged with post data rest_params = { 'verb': 'comment', 'object': { 'inReplyTo': [{ '_id': ObjectId(activityid), 'objectType': refering_activity.object['objectType'] }] } } # Initialize a Activity object from the request newactivity = Activity() newactivity.fromRequest(request, rest_params=rest_params) code = 201 newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid comment = dict(newactivity.object) comment['published'] = newactivity.published comment['author'] = request.actor comment['id'] = newactivity._id del comment['inReplyTo'] refering_activity.addComment(comment) handler = JSONResourceEntity(newactivity.flatten(), status_code=code) return handler.buildResponse()
def subscribe(context, request): """ /people/{username}/subscriptions """ # XXX For now only one context can be subscribed at a time actor = request.actor rest_params = {'actor': actor, 'verb': 'subscribe'} # Initialize a Activity object from the request newactivity = Activity() newactivity.fromRequest(request, rest_params=rest_params) #Check if user is already subscribed subscribed_contexts_hashes = [a['urlHash'] for a in actor.subscribedTo['items']] if sha1(newactivity.object['url']).hexdigest() in subscribed_contexts_hashes: # If user already subscribed, send a 200 code and retrieve the original subscribe activity # post when user was susbcribed. This way in th return data we'll have the date of subscription code = 200 activities = MADMaxCollection(context.db.activity) query = {'verb': 'subscribe', 'object.url': newactivity.object['url'], 'actor.username': actor.username} newactivity = activities.search(query)[-1] # Pick the last one, so we get the last time user subscribed (in case a unsbuscription occured sometime...) else: # If user wasn't created, 201 indicates that the subscription has just been added code = 201 newactivity_oid = newactivity.insert() # Insert a subscribe activity newactivity['_id'] = newactivity_oid #Register subscription to the actor contexts = MADMaxCollection(context.db.contexts, query_key='urlHash') scontext = contexts[sha1(newactivity['object']['url']).hexdigest()] actor.addSubscription(scontext) handler = JSONResourceEntity(newactivity.flatten(), status_code=code) 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()