def addComment(self, comment): """ Adds a comment to an existing activity and updates refering activity keywords and hashtags """ # Clean innecessary fields non_needed_actor_fields = [ "talkingIn", "subscribedTo", "following", "last_login", "_id", "published", "twitterUsername", ] for fieldname in non_needed_actor_fields: if fieldname in comment["actor"]: del comment["actor"][fieldname] self.add_to_list("replies", comment, allow_duplicates=True) self.setKeywords() activity_hashtags = self["object"].setdefault("_hashtags", []) activity_hashtags.extend(comment.get("_hashtags", [])) self["object"]["_hashtags"] = list(set(activity_hashtags)) self["lastComment"] = ObjectId(comment["id"]) self.save() notify = self.get("contexts", [{}])[0].get("notifications", False) if notify in ["comments"]: notifier = RabbitNotifications(self.request) notifier.notify_context_activity_comment(self, comment)
def _after_subscription_remove(self, username): """ Removes rabbitmq bindings after new subscription """ save_context = False # Remove leaving participant from participants list ONLY for group conversations of >=2 participants if len(self['participants']) >= 2 and 'group' in self.get('tags', []): self['participants'] = [user for user in self['participants'] if user['username'] != self.request.actor['username']] save_context = True # Tag no-group conversations that will be left as 1 participant only as single if len(self['participants']) == 2: self.setdefault('tags', []) if 'group' not in self['tags']: if 'single' not in self['tags']: self['tags'].append('single') save_context = True # Tag group conversations that will be left as 1 participant only as archived if len(self['participants']) == 1: self.setdefault('tags', []) if 'group' in self['tags']: if 'archive' not in self['tags']: self['tags'].append('archive') save_context = True if save_context: self.save() notifier = RabbitNotifications(self.request) notifier.unbind_user_from_conversation(self, username)
def _after_subscription_add(self, username): """ Creates rabbitmq bindings after new subscription """ if self.get("notifications", False): notifier = RabbitNotifications(self.request) notifier.bind_user_to_context(self, username)
def modifyContext(self, properties): """Update the user object with the given properties""" # If updating the twitterUsername, get its Twitter ID if properties.get("twitterUsername", None): api = get_twitter_api(self.request.registry) properties["twitterUsernameId"] = get_userid_from_twitter(api, properties["twitterUsername"]) self.updateFields(properties) if self.get("twitterUsername", None) is None and self.get("twitterUsernameId", None) is not None: del self["twitterUsernameId"] # someone changed notifications settings for this context if "notifications" in properties: notifier = RabbitNotifications(self.request) if self.get("notifications", False): for user in self.subscribedUsers(): notifier.bind_user_to_context(self, user["username"]) else: for user in self.subscribedUsers(): notifier.unbind_user_from_context(self, user["username"]) if "url" in properties: self["hash"] = sha1(self["url"]).hexdigest() self.save()
def _after_insert_object(self, oid): # notify activity if the activity is from a context # with enabled notifications self["lastComment"] = oid self.save() notify = self.get("contexts", [{}])[0].get("notifications", False) if notify in ["posts", "comments", True]: notifier = RabbitNotifications(self.request) notifier.notify_context_activity(self)
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 _after_insert_object(self, oid, notifications=True): """ Create user exchanges just after user creation on the database """ query = { 'objectType': 'conversation', 'participants.username': self['username'] } conversations_search = self.request.db.conversations.search(query) for conversation in conversations_search: if len(conversation['participants']) == 2: if 'group' not in conversation['tags']: if 'archive' in conversation['tags']: conversation['tags'].remove('archive') conversation['tags'].append('single') conversation.save() if notifications: notifier = RabbitNotifications(self.request) notifier.add_user(self['username'])
def _after_delete(self): """ Deletes user exchanges just after user is deleted. """ notifier = RabbitNotifications(self.request) notifier.delete_user(self['username'])
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 _after_delete(self): notifier = RabbitNotifications(self.request) notifier.unbind_conversation(self)
def _after_subscription_add(self, username): """ Creates rabbitmq bindings after new subscription """ notifier = RabbitNotifications(self.request) notifier.bind_user_to_conversation(self, username)
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 _after_subscription_remove(self, username): """ Removes rabbitmq bindings after new subscription """ notifier = RabbitNotifications(self.request) notifier.unbind_user_from_context(self, username)
def _after_saving_object(self, oid): if self.field_changed("twitterUsername"): notifier = RabbitNotifications(self.request) notifier.restart_tweety()