def follower_invite_trusted(followed, follower, delay_commit=False): followed = get_member(followed) follower = get_member(follower) if not followed: raise action_error(_('unable to find followed'), code=404) if not follower: raise action_error(_('unable to find follower'), code=404) if follower == followed: raise action_error(_('may not follow yourself'), code=400) #if followed in follower.following: if follower.is_following(followed): raise action_error(_('already following'), code=400) if follower.is_follow_trusted_inviter(followed): raise action_error(_('already invited to follow as trusted')) follow = Follow() follow.member = followed follow.follower = follower follow.type = 'trusted_invite' Session.add(follow) if not delay_commit: Session.commit() #invalidate_member(follower) #invalidate_member(followed) follower.send_notification( messages.follow_invite_trusted(member=followed, you=follower)) return True
def associate_janrain_account(user, type, token): """ Associate a login record for a Janrain account This is called at: 1.) Registration 2.) Linking multiple login accounts to a single Civicboom account """ login = None try: login = Session.query(UserLogin).filter(UserLogin.token == token).filter(UserLogin.type == type).one() except: pass if login: if login.user == user: return # If login already belongs to this user then abort if login.user: # Warn existing user that account is being reallocated login.user.send_email(subject=_('login account reallocated'), content_text=_('your %s account has been allocated to the user %s') % (type, user.username)) if not config['development_mode']: janrain('unmap', identifier=login.token, primaryKey=login.member_id) login.user = user else: login = UserLogin() login.user = user login.type = type login.token = token Session.add(login) Session.commit() if not config['development_mode']: janrain('map', identifier=login.token, primaryKey=login.member_id) # Let janrain know this users primary key id, this is needed for agrigation posts
def parent_disassociate(content, delay_commit=False): if not content.parent: return False # Update has to be done before the commit in this case bcause the parent is needed #invalidate_content(content.parent) # Could update responses in the future, but for now we just invalidate the whole content #invalidate_content(content) # this currently has code to update parents reponses, is the line above needed? if content.__type__ == 'comment': content.creator.send_notification( messages.comment_dissassociated( parentcreator=content.parent.creator, parentcontent=content.parent)) content.visible = False else: content.creator.send_notification( messages.article_disassociated_from_assignment( member=content.parent.creator, article=content, assignment=content.parent)) # TODO: what if an assignment is dissasocaited from an assignment? if content.__type__ == 'article': content.approval = "dissassociated" content.parent = None if not delay_commit: Session.commit() return True
def withdraw_assignemnt(assignment, member, delay_commit=False): member = get_member(member) assignment = get_content(assignment) if not member: raise action_error(_("cant find user"), code=404) if not assignment: raise action_error(_("cant find assignment"), code=404) try: assignment_accepted = Session.query(MemberAssignment).filter_by( member_id=member.id, content_id=assignment.id, status="accepted").one() assignment_accepted.status = "withdrawn" #Session.update(assignment_accepted) Session.commit() #invalidate_accepted_assignment(member) assignment.creator.send_notification( messages.assignment_interest_withdrawn(member=member, assignment=assignment, you=assignment.creator)) return True except: pass return False
def rate_content(content, member, rating): content = get_content(content) member = get_member(member) rating_value = int(rating) if not content: raise action_error(_('unable to find content'), code=404) if not member: raise action_error(_('unable to find member'), code=404) if rating and rating_value < 0 or rating_value > 5: raise action_error(_("Ratings can only be in the range 0 to 5"), code=400) # remove any existing ratings # we need to commit after removal, otherwise SQLAlchemy # will optimise remove->add as modify-existing, and the # SQL trigger will break try: existing = Session.query(Rating).filter( Rating.content == content).filter(Rating.member == member).one() Session.delete(existing) Session.commit() except NoResultFound: pass # rating = 0 = remove vote # add a new one if rating_value > 0: r = Rating() r.content = content r.member = member r.rating = rating_value Session.add(r) Session.commit()
def unfollow(follower, followed, delay_commit=False): followed = get_member(followed) follower = get_member(follower) if not followed: raise action_error(_('unable to find followed'), code=404) if not follower: raise action_error(_('unable to find follower'), code=404) #if followed not in follower.following: # GregM: can unfollow to remove trusted invite if not follower.is_following( followed) and not follower.is_follow_trusted_inviter(followed): raise action_error(_('not currently following'), code=400) #follower.following.remove(followed) follow = Session.query(Follow).filter( Follow.member_id == followed.id).filter( Follow.follower_id == follower.id).one() Session.delete(follow) if not delay_commit: Session.commit() #invalidate_member(follower) #invalidate_member(followed) followed.send_notification( messages.follow_stop(member=follower, you=followed)) return True
def set_password(user, new_token, delay_commit=False): """ Set password WARNING! We assume the user has already been authenticated - remove old password (if found) - create new password record """ # search for existing record and remove it # try: for existing_login in [login for login in user.login_details if login.type == 'password']: log.debug("removing password for %s" % user.username) #if existing_login.token == old_token: raise Exception('old password token does not match - aborting password change') Session.delete(existing_login) log.debug("removed ok") #try: Session.execute(UserLogin.__table__.delete().where(and_(UserLogin.__table__.c.member_id == user.id, UserLogin.__table__.c.token == token))) except Exception: pass # Set new password u_login = UserLogin() u_login.user = user u_login.type = 'password' u_login.token = new_token Session.add(u_login) if not delay_commit: Session.commit()
def follower_distrust(followed, follower, delay_commit=False): followed = get_member(followed) follower = get_member(follower) if not followed: raise action_error(_('unable to find followed'), code=404) if not follower: raise action_error(_('unable to find follower'), code=404) if not follower.is_following(followed): raise action_error(_('not currently following'), code=400) follow = Session.query(Follow).filter( Follow.member_id == followed.id).filter( Follow.follower_id == follower.id).one() if not follow: raise action_error(_('member is not a follower'), code=404) if follow.type == 'trusted': follow.type = 'normal' elif follow.type == 'trusted_invite': raise action_error(_('follower still has pending invite'), code=400) elif follow.type == 'normal': raise action_error(_('follower was not trusted'), code=400) if not delay_commit: Session.commit() # invalidate_member(follower) # GregM: Needed? # invalidate_member(followed) # GregM: Needed? follower.send_notification( messages.follower_distrusted(member=followed, you=follower)) return True
def respond_assignment(parent_content, member, delay_commit=False): """ When creating a response, the accepted record should be flagged as responded """ member = get_member(member) parent_content = get_content(parent_content) if not member: raise action_error(_("cant find user"), code=404) if not parent_content: raise action_error(_("cant find parent content"), code=404) if parent_content.__type__ != 'assignment': return # Nothing to do if parent is not assignment try: # upgrade an 'accepted' record to 'responded' member_assignment = Session.query(MemberAssignment).filter_by( member_id=member.id, content_id=parent_content.id).one() member_assignment.status = "responded" except: # otherwise create 'responded' record member_assignment = MemberAssignment() member_assignment.content = parent_content member_assignment.member = member #assignment_accepted.member_id = member.id member_assignment.status = "responded" Session.add(member_assignment) if not delay_commit: Session.commit() #invalidate_accepted_assignment(member) return True
def payment_member_remove(payment_account, member): member = get_member(member) if not member.payment_account == payment_account: return False member.payment_account = None Session.commit() return True
def payment_member_add(payment_account, member): member = get_member(member) if member.payment_account: return False member.payment_account = payment_account Session.commit() return True
def parent_approve(content, delay_commit=False): content.edit_lock = "parent_owner" content.approval = "approved" # AllanC - the direct email notifications were removed. They have now been replaced by the notification system # Email content parent & content creator #from pylons import tmpl_context as c # Needed for passing varibles to templates #c.content = content #content.parent.creator.send_email(subject=_('content request' ), content_html=render('/email/approve_lock/lock_article_to_organisation.mako')) #content.creator.send_email( subject=_('content approved'), content_html=render('/email/approve_lock/lock_article_to_member.mako')) # Generate notifications - these have custom email templates that override the base notification extra_vars = { 'content_id': content.id } #extra variables for template rendering content.creator.send_notification( messages.article_approved_creator(member=content.parent.creator, parent=content.parent, content=content), extra_vars=extra_vars) #, delay_commit=True content.parent.creator.send_notification( messages.article_approved_parent(member=content.parent.creator, parent=content.parent, content=content), extra_vars=extra_vars) #, delay_commit=True if not delay_commit: Session.commit() #invalidate_content(content) return True
def viewable_by(self, member): """ Check to see if a member object has the rights to view this content """ # TODO check groups of creator to see if member is in the owning group if self.editable_by(member): return True # Always allow content to be viewed by owners/editors if self.__type__ == "draft": return False # if draft, only editors (above) can see if self.__type__ == "comment": return self.parent.viewable_by( member) # if comment, show if we can see the parent article if self.visible == True: # GregM: If current content has a root parent then check viewable_by on root parent root = self.root_parent if root: return root.viewable_by(member) # GregM: If content is private check member is a trusted follower of content's creator # We NEED to check has accepted, invited etc. for requests if self.private == True: if self.creator.is_follower_trusted(member): return True elif self.__type__ == "assignment": from civicboom.lib.database.get_cached import get_assigned_to member_assignment = get_assigned_to(self, member) if member_assignment: if not member_assignment.member_viewed: from civicboom.model.meta import Session member_assignment.member_viewed = True Session.commit() return True return False return True return False
def del_group(group): group = get_group(group) from pylons import tmpl_context as c for member in [member_role.member for member_role in group.members_roles]: member.send_notification( messages.group_deleted(group=group, admin=c.logged_in_user) ) # AllanC - We cant use the standard group.send_notification because the group wont exisit after this line! Session.delete(group) Session.commit()
def parent_seen(content, delay_commit=False): content.edit_lock = "parent_owner" content.approval = "seen" # AllanC - TODO generate notification #content.creator.send_notification(???, delay_commit=True) if not delay_commit: Session.commit() #invalidate_content(content) return True
def unboom_content(content, member, delay_commit=False): boom = has_boomed(content, member) if boom: Session.delete(boom) else: raise action_error(_( "%s has not boomed previously boomed this _content" % member), code=400) if not delay_commit: Session.commit()
def verify_email_hash(user, hash, commit=False): user = get_member(user) if user and user.hash() == hash: if not config['demo_mode']: # AllanC - Demo mode is ALWAYS offline, there is no way we can validate members emails address's. But the hash is correct so return True if user.email_unverified: user.email = user.email_unverified user.email_unverified = None if commit: Session.commit() return True return False
def part_pay_invoice_manual(self, invoice, amount=None): """ Create a manual transaction against invoice """ txn = BillingTransaction() txn.invoice = invoice txn.status = 'complete' txn.amount = amount or invoice.total_due txn.provider = 'manual_test' txn.reference = 'test' Session.commit() pass
def morph_content_to(content, after_type): """ params: content can be a Content object or a string id - as this works at the database level, any content data not commited to the DB before this call will be lost after_type is a string of the type the object is being transformed too return: the new mophed object from the database Notes: This is a VERY expensive operation as it requires up to 3 calls to get_content, each of these calls is joining potentialy over 3 tables """ content = get_content(content) if not content: raise Exception('no content to morph') if content.__type__ == None: return content # If we don't know the source object type then we cant process it if content.__type__ == after_type: return content # If the before and after types are the same then return the content obj as no processing needs to take place if content.id == None: # If the content has not been commited to the DB # then return an object of the correct type? # todo? #log.warn('content to morph not in DB? investigate') raise Exception('content to morph not in DB? investigate') id = content.id prev_type = content.__type__ sql_generator_key = content.__type__+":"+after_type if sql_generator_key not in morph_sql: raise Exception('unable to morph content from %s to %s' % (content.__type__, after_type) ) Session.expunge(content) # Remove content from SQLAlchemys scope for sql_cmd in morph_sql[sql_generator_key](id): Session.execute(sql_cmd) Session.commit() content = get_content(id) assert content.__type__ == after_type # If this is not true then something has gone very wrong! # AllanC - Although the events linked to the commit of this object invalidte the list type for the 'current' content type, they do not take into consideration a content morph from the previous type # In fact .. we need to invalidate the content item manually here because the SQL statement does not associate with the SQLA object and therefor does not trigger the SQLA events # We need to manually invalidate the list that this content object was 'before' the morph invalidate_content(content) invalidate_content_list(prev_type, content.creator_id) return content
def delete_content(self, id, force=False): if force: # ignore logged in user and just get rid of it Session.delete(Session.query(Content).get(id)) Session.commit() else: self.assertIn('delete', self.get_actions(id)) response = self.app.post(url('content', id=id, format="json"), params={ '_method': 'delete', '_authentication_token': self.auth_token, }, status=200)
def accept_assignment(assignment, member, status="accepted", delay_commit=False): member = get_member(member) assignment = get_content(assignment) if not member: raise action_error(_("cant find user"), code=404) if not assignment: raise action_error(_("cant find assignment"), code=404) if not issubclass(assignment.__class__, AssignmentContent): raise action_error(_("only _assignments can be accepted"), code=400) # all permissins hendled by controler action - so this is unneeded here #if not assignment.viewable_by(c.logged_in_persona): # raise action_error(_('_assignment is not visible to your user and therefor cannot be accepted'), code=403) if assignment_previously_accepted_by(assignment, member): raise action_error(_( '_assignment has been previously accepted and cannot be accepted again' ), code=400) #if assignment.creator == member: # raise action_error(_("cannot accept your own _assignment"), code=400) assignment_accepted = MemberAssignment() assignment.assigned_to.append(assignment_accepted) assignment_accepted.member = member assignment_accepted.status = status Session.add(assignment_accepted) if not delay_commit: Session.commit() #invalidate_accepted_assignment(member) if status == "accepted": assignment.creator.send_notification( messages.assignment_accepted(member=member, assignment=assignment, you=assignment.creator)) if status == "pending": member.send_notification( messages.assignment_invite(member=assignment.creator, assignment=assignment, you=member)) return True
def mark_messages_as_read(self, **kwargs): """ POST /profile/{id}/mark_messages_as_read: Mark messages as read @type action @api contents 1.0 (WIP) @param type type of message object to mark as read from all, to, notification """ list_type = kwargs.get('list', 'all') if list_type in list_filters and list_type in ['all','to','notification']: results = Session.query(Message).filter(Message.target_id==c.logged_in_persona.id) results = list_filters[list_type](results) for message in results.all(): message.read = True Session.commit() return action_ok(message='%s marked as read' % type, code=201) raise action_error(_('list %s not supported') % kwargs.get('list'), code=400)
def remove_member(group, member, delay_commit=False): group = get_group(group) member = get_member(member) membership = get_membership(group, member) if not group: raise action_error(_('unable to find group'), code=404) if not member: raise action_error(_('unable to find member'), code=404) if not membership: raise action_error(_('not a member of group'), code=400) # AllanC - permissions moved to controller #if member!=c.logged_in_persona and not group.is_admin(c.logged_in_persona): # raise action_error('current user has no permissions for this group', 403) #AllanC - integrety moved to model #if membership.role=="admin" and num_admins<=1: # raise action_error('cannot remove last admin', 400) # Notifications if membership.status == "active": # member removed from pylons import tmpl_context as c if member != c.logged_in_user: membership.member.send_notification( messages.group_remove_member_to_member(admin=c.logged_in_user, group=group)) group.send_notification( messages.group_remove_member_to_group(admin=c.logged_in_user, group=group, member=member)) elif membership.status == "invite": # invitation declined group.send_notification( messages.group_invitation_declined(member=member, group=group)) elif membership.status == "request": # request declined membership.member.send_notification( messages.group_request_declined(group=group)) Session.delete(membership) if not delay_commit: Session.commit() #invalidate_member(group) #invalidate_member(member) return True
def set_role(group, member, role, delay_commit=False): group = get_group(group) member = get_member(member) membership = get_membership(group, member) role = role or membership.role or group.default_role if not group: raise action_error(_('unable to find group'), code=404) if not member: raise action_error(_('unable to find member'), code=404) if not membership: raise action_error(_('not a member of group'), code=400) if role not in group_member_roles.enums: raise action_error('not a valid role', code=400) # AllanC - permisions moved to controller #if not group.is_admin(c.logged_in_persona): # raise action_error(_('no permissions for this group'), 403) # AllanC - integrtiy moved to model #if membership.role=="admin" and num_admins<=1: # raise action_error('cannot remove last admin', 400) from pylons import tmpl_context as c membership.role = role if membership.status == "request": membership.status = "active" member.send_notification( messages.group_request_accepted(admin=c.logged_in_user, group=group, you=member)) group.send_notification( messages.group_new_member(member=member, group=group)) else: group.send_notification( messages.group_role_changed(admin=c.logged_in_user, member=member, group=group, role=role)) if not delay_commit: Session.commit() #invalidate_member(group) #invalidate_member(member) return True
def set_payment_account(member, value, delay_commit=False): member = get_member(member) #account = None if isinstance(value, PaymentAccount): member.payment_account = value elif value in account_types.enums: if value == 'free': account = None else: account = PaymentAccount() account.type = value Session.add(account) member.payment_account = account else: raise action_error('unknown account type: %s' % value) if not delay_commit: Session.commit() return True
def add_to_interests(member, content, delay_commit=False): content = get_content(content) member = get_member(member) if not content: raise action_error(_('unable to find content'), code=404) if not member: raise action_error(_('unable to find member'), code=404) #AllanC - TODO: humm ... if we have a duplicate entry being added it will error ... we want to suppress that error member.interest.append(content) # Could update "recomended" feed with new criteria? if not delay_commit: Session.commit() return True
def invite(self, members, **kwargs): """ For closed assignments we need to invite specific members to participate invite can be given a single member or a list of members (as username strings or member object list) """ from civicboom.lib.database.actions import accept_assignment from civicboom.model.meta import Session def invite_member(member): return accept_assignment(self, member, status="pending", delay_commit=True) if isinstance(members, list): for member in members: invite_member(member) else: invite_member(members) Session.commit()
def join_group(group, member, delay_commit=False): return_value = True group = get_group(group) member = get_member(member) membership = get_membership(group, member) if not group: raise action_error(_('unable to find group'), code=404) if not member: raise action_error(_('unable to find member to add'), code=404) # AllanC - join permissions moved to controller if membership and membership.status == "invite": membership.status = "active" else: membership = GroupMembership() membership.group = group membership.member = member membership.role = group.default_role group.members_roles.append(membership) # If a join request if group.join_mode == "invite_and_request": membership.status = "request" return_value = "request" group.send_notification( messages.group_join_request(member=member, group=group)) # If user has actually become a member (open or reply to invite) then notify group if return_value == True: group.send_notification( messages.group_new_member(member=member, group=group)) if not delay_commit: Session.commit() #invalidate_member(group) #invalidate_member(member) return return_value
def follow(follower, followed, delay_commit=False): followed = get_member(followed) follower = get_member(follower) if not followed: raise action_error(_('unable to find followed'), code=404) if not follower: raise action_error(_('unable to find follower'), code=404) if follower == followed: raise action_error(_('may not follow yourself'), code=400) #if followed in follower.following: if follower.is_following(followed): raise action_error(_('already following'), code=400) # AllanC - I wanted to use is_following and remove the following reference - but as this code is run by base test populator before the site is running it cant be # GregM: Change invite to follow if is invited, otherwise follow: if follower.is_follow_trusted_inviter(followed): follow = Session.query(Follow).filter( Follow.member_id == followed.id).filter( Follow.follower_id == follower.id).filter( Follow.type == 'trusted_invite').one() follow.type = 'trusted' else: #follower.following.append(followed) follow = Follow() follow.member = followed follow.follower = follower Session.add(follow) if not delay_commit: Session.commit() #invalidate_member(follower) #invalidate_member(followed) followed.send_notification( messages.followed_by(member=follower, you=followed)) return True
def invite(group, member, role, delay_commit=False): group = get_group(group) member = get_member(member) membership = get_membership(group, member) role = role or group.default_role if not group: raise action_error(_('unable to find group'), code=404) if not member: raise action_error(_('unable to find member'), code=404) if membership: raise action_error(_('already a member of group'), code=400) if role not in group_member_roles.enums: raise action_error('not a valid role', code=400) # AllanC - permissions moved to controller #if not group.is_admin(c.logged_in_persona): # raise action_error(_('no permissions for this group'), 403) membership = GroupMembership() membership.group = group membership.member = member membership.role = role membership.status = "invite" group.members_roles.append(membership) # Notification of invitation from pylons import tmpl_context as c member.send_notification( messages.group_invite(admin=c.logged_in_user, group=group, role=membership.role, you=member)) if not delay_commit: Session.commit() #invalidate_member(group) #invalidate_member(member) return True