예제 #1
0
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()
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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()
예제 #5
0
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
예제 #6
0
def get_tag(tag):
    """
    Returns a tag object for the string passed to it
    If it does not appear in the database then return a new tag object
    If it does exisit in the data then return the database object
    """
    tag = tag.lower()
    try:
        return Session.query(Tag).filter_by(name=unicode(tag)).one()
    except NoResultFound as nrf:
        t = Tag(unicode(tag))
        Session.add(t)
        return t
예제 #7
0
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
예제 #8
0
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
예제 #9
0
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
예제 #10
0
def boom_content(content, member, delay_commit=False):
    # Validation
    if content.private == True:
        raise action_error(_("cannot boom private content"), code=400)
    if has_boomed(content, member):
        raise action_error(_("You have previously boomed this _content"),
                           code=400)

    boom = Boom()
    #boom.content_id = content.id
    #boom.member_id  = member.id
    boom.content = content
    boom.member = member
    Session.add(boom)

    if not delay_commit:
        Session.commit()

    if content.__type__ == 'article':
        member.send_notification_to_followers(
            messages.boom_article(member=member, article=content))
    elif content.__type__ == 'assignment':
        member.send_notification_to_followers(
            messages.boom_assignment(member=member, assignment=content))
예제 #11
0
    def test_summary_emails(self):
        
        def task_summary_notification_email():
            response = self.app.get(url(controller='task', action='summary_notification_email'))
            self.assertIn(response_completed_ok, response.body)
        
        now_start = self.server_datetime()
        now = now_start
        
        # No summary emails should trigger yet because no users have setup an interval
        num_emails = getNumEmails()
        task_summary_notification_email()
        self.assertEquals(num_emails, getNumEmails())
        
        # Setup test data ------------------------------------------------------
        self.setting('summary_email_interval', 'advanced', 'hours=1') # Setup summary date
        
        # Execute timed task ---------------------------------------------------
        num_emails = getNumEmails()
        task_summary_notification_email()
        
        # Add message that should trigger in last hour and send notification
        m1 = Message()
        m1.target    = Session.query(User).get('unittest')
        m1.subject   = 'Test summary_notification_email notification'
        m1.content   = 'Test summary_notification_email notification'
        m1.timestamp = now + datetime.timedelta(minutes=30, hours=1)
        Session.add(m1)
        m2 = Message()
        m2.source    = Session.query(User).get('kitten')
        m2.target    = Session.query(User).get('unittest')
        m2.subject   = 'Test summary_notification_email message'
        m2.content   = 'Test summary_notification_email message'
        m2.timestamp = now + datetime.timedelta(minutes=15, hours=1)
        Session.add(m2)
        m3 = Message()
        m3.source    = Session.query(User).get('unittest')
        m3.target    = Session.query(User).get('kitten')
        m3.subject   = 'To Amy kitten'
        m3.content   = 'To Amy kitten'
        m3.timestamp = now + datetime.timedelta(minutes=20, hours=1)
        Session.add(m2)

        Session.commit()
        
        now = self.server_datetime(now + datetime.timedelta(hours=2))
        
        num_emails = getNumEmails()
        task_summary_notification_email()
        
        # Check sent emails ----------------------------------------------------
        
        self.assertEquals(getNumEmails(), num_emails + 1)
        email = getLastEmail().content_text
        self.assertIn   ('summary_notification_email notification', email)
        self.assertIn   ('summary_notification_email message'     , email) 
        self.assertIn   ('To Amy'                                 , email) # Check sent message appears in summary
        self.assertIn   ('Amy M'                                  , email) # Check the avatar and names of targets are aquired from DB
        self.assertNotIn('unitfriend'                             , email) # Loose check to try to catch if any other notifications bleed over into this summary email
        
        # Check no emails sent if outside interval -----------------------------
        now = self.server_datetime(now + datetime.timedelta(hours=20))
        num_emails = getNumEmails()
        task_summary_notification_email()
        self.assertEquals(num_emails, getNumEmails())
        
        # Cleanup --------------------------------------------------------------
        
        # Reset db state
        #self.setting('summary_email_interval', 'advanced', 'hours=0', assert_set_value=False) # hours=0 turns into None and this breaks the auto assertion at the end of set value
        self.setting('summary_email_interval', 'advanced', '', assert_set_value=False) #AllanC - an empty string should default to None
        
        # No summary emails should trigger yet because no users have setup an interval
        num_emails = getNumEmails()
        task_summary_notification_email()
        self.assertEquals(num_emails, getNumEmails()) # AllanC - this fails .. investigation needs to be made
        
        # Reset server datetime
        self.server_datetime(now_start)
        
        # Delete test messages
        Session.delete(m1)
        Session.delete(m2)
        Session.delete(m3)
        Session.commit()
예제 #12
0
def send_notification(members, message):  #members, rendered_message
    """
    Threaded message system
    Save and handles propogating the message to different technologies for all members of a group or an indvidual
    """

    #log.debug('Running send_notification to members=%s message=%s' % (members,message))

    message['source'] = get_member(
        message.get('source') or message.get('source_id')) or message.get(
            'source')  # Attempt to normalize source member

    # Multiple memebrs
    if isinstance(members, list):

        for member in get_members(members, expand_group_members=True):
            message[
                'target_username'] = member.id  # Overlay the direct target member of this message as an additional param
            send_notification(member, message)
            #if member.__type__ == 'group':
            #    send_notification(get_group_member_username_list(member), **kwargs)
        Session.commit(
        )  # No need to commits as all workers commit at end by default

    # Single member
    else:
        member = get_member(members)

        # AllanC - Messages can be passed without a default_route or name if they are not auto generated notifications
        #          in this case they are deemed "user to user" messages and have enforced default template and route
        if 'default_route' not in message:
            message['default_route'] = 'e'
        if 'name' not in message:
            message['name'] = 'message'

        # Get propergate settings - what other technologies is this message going to be sent over
        # Attempt to get routing settings from the member's config; if that fails, use the
        # message's default routing
        message_tech_options = member.config.get(
            "route_" + message.get('name', 'default'),
            message.get('default_route'))

        # Iterate over each letter of tech_options - each letter is a code for the technology to send it to
        # e.g 'c'  will send to Comufy
        #     'et' will send to email and twitter
        #     'n'  is a normal notification
        #     ''   will dispose of the message because it has nowhere to go
        for route in message_tech_options:

            # -- Comufy --------------------------------------------------------
            # very unused, untested, commented out for coverage
            #if route == 'c':
            #    pass

            # -- Email ---------------------------------------------------------
            if route == 'e':
                if member.__type__ == 'user':  # Only send emails to individual users

                    # Feature #498 - Check for existing email template and attempt to render
                    def notification_template(template):
                        template_path = os.path.join("email", "notifications",
                                                     template + ".mako")
                        #if os.path.exists(os.path.join(config['path.templates'], template_path)):
                        if os.path.exists(
                                os.path.join("civicboom/templates",
                                             template_path)):
                            return template_path
                        return 'email/notifications/default.mako'

                    #if config['debug'] == True or config['debug'] == "true": # debug is a string, so config['debug'] == "false" == Trues
                    if config['worker.queue.type'] == 'inline':
                        c = render_mako(notification_template(
                            message.get('name')),
                                        extra_vars={
                                            "kwargs": message,
                                        })
                    else:  # pragma: no cover - test mode uses inline rendering, this is stand-alone
                        l = TemplateLookup(
                            directories=['.', 'civicboom/templates'],
                            input_encoding='utf-8',
                            output_encoding='utf-8')
                        f = os.path.join(
                            "civicboom/templates",
                            notification_template(message.get('name')))
                        t = Template(filename=f, lookup=l)
                        c = t.render_unicode(kwargs=message, h=helpers)
                    send_email(
                        member,
                        subject=message.get(
                            'subject'),  #, _('_site_name notification')
                        content_html=c,
                    )

            # -- Notification --------------------------------------------------
            # Save message in message table (to be seen as a notification)
            if route == 'n':
                m = Message()
                m.subject = message['subject']
                m.content = message['content']
                m.target = member
                Session.add(m)
                #member.messages_to.append(m)
                #invalidate_list_version('mesages_index', 'notification', m.target.id) # AllanC - we can replace this invalidation with a sqlalchemy list watching trigger maybe?

            # ------------------------------------------------------------------

        log.debug("%s was sent the message '%s', routing via %s" %
                  (member.username, message.get('name'), message_tech_options))

    return True
예제 #13
0
def flag(obj,
         raising_member=None,
         type="automated",
         comment=None,
         url_base=None,
         delay_commit=False,
         moderator_address=None):
    """
    if url_base is included an alternate URL generator to avert the use of the pylons one
    """
    flag = FlaggedEntity()
    flag.raising_member = get_member(raising_member)

    if isinstance(obj, Content):
        flag.offending_content = obj
    if isinstance(obj, Member):
        flag.offending_member = obj
    if isinstance(obj, Message):
        flag.offending_message = obj

    flag.comment = strip_html_tags(comment)
    flag.type = type
    Session.add(flag)
    if not delay_commit:
        Session.commit()
    else:
        Session.flush()

    # Send email to alert moderator
    raising_member_username = '******'
    try:
        raising_member_username = flag.raising_member.id
    except:
        pass

    # Base email text
    email_text_dict = {
        "raising_member": raising_member_username,
        "type": type,
        "comment": flag.comment,
        "action_ignore": '',
        "action_delete": '',
    }
    email_text = """
--- Report ---

Reporter: %(raising_member)s
Category: %(type)s

%(comment)s

--- Actions ---

If the content is ok, click here to remove the flag:
  %(action_ignore)s

If the content is not ok, click here to hide it from the site:
  %(action_delete)s

"""

    # Additional Content text
    if flag.offending_content:
        email_text_dict.update({
            "creator_name":
            flag.offending_content.creator.id,
            "creator_url":
            url('member',
                id=flag.offending_content.creator.id,
                qualified=True,
                sub_domain="www"),
            "content_url":
            url('content',
                id=flag.offending_content.id,
                qualified=True,
                sub_domain="www"),
            "content_title":
            flag.offending_content.title,
            "content_body":
            flag.offending_content.content,
            "action_ignore":
            url("admin/moderate?kay=yay&content_id=%s" %
                flag.offending_content.id,
                qualified=True),  #sub_domain="www"),
            "action_delete":
            url("admin/moderate?kay=nay&content_id=%s" %
                flag.offending_content.id,
                qualified=True),  #sub_domain="www"),
        })
        email_text = email_text + """
--- Reported Content ---

Title:  %(content_title)s
        %(content_url)s
Author: %(creator_name)s
        %(creator_url)s

%(content_body)s
"""

    if flag.offending_member:
        log.error('member flaging not implemented yet')

    if flag.offending_message:
        log.error('message flaging not implemented yet')

    email_text = email_text % email_text_dict
    send_email(
        moderator_address,
        subject='flagged content [%s]' % type,
        content_text=email_text,
        content_html="<pre>" + email_text + "</pre>",
    )
예제 #14
0
def upgrade_user_to_group(member_to_upgrade_to_group,
                          new_admins_username,
                          new_group_username=None):
    """
    Only to be called by admins/power users
    This handled the migration of users to groups at an SQL level
    """
    to_group = get_member(member_to_upgrade_to_group)
    admin_user = get_member(new_admins_username)

    # Validation
    if not to_group or to_group.__type__ != 'user':
        raise action_error('member_to_upgrade_to_group not a group', code=404)
    if get_member(new_group_username):
        raise action_error('new_group_username already taken', code=404)
    if admin_user:
        raise action_error('new_admins_username already taken', code=404)

    # Create new admin user
    admin_user = User()
    admin_user.id = new_admins_username
    admin_user.name = new_admins_username
    admin_user.status = 'active'
    admin_user.email = to_group.email
    admin_user.email_unverifyed = to_group.email_unverified
    Session.add(admin_user)
    Session.commit()  # needs to be commited to get id

    sql_cmds = []

    if new_group_username:
        sql_cmds += [
            Member.__table__.update().where(
                Member.__table__.c.id == to_group.id).values(
                    username=new_group_username),
        ]

    sql_cmds += [
        # UPDATE  member set username='******', __type__='group' WHERE id=533;
        Member.__table__.update().where(Member.__table__.c.id == to_group.id
                                        ).values(__type__='group'),

        # Reassign the login details from the old member to the new admin user
        UserLogin.__table__.update().where(
            UserLogin.__table__.c.member_id == to_group.id
        ).values(member_id=admin_user.id),

        # DELETE the matching user record that pairs with the member record
        User.__table__.delete().where(User.__table__.c.id == to_group.id),

        # INSERT matching group record to pair group name
        Group.__table__.insert().values(id=to_group.id,
                                        join_mode='invite',
                                        member_visibility='private',
                                        default_content_visibility='public',
                                        default_role='admin',
                                        num_members=1),

        # INSERT admin_user as as admin of the group
        GroupMembership.__table__.insert().values(group_id=to_group.id,
                                                  member_id=admin_user.id,
                                                  role='admin',
                                                  status='active'),
    ]

    for sql_cmd in sql_cmds:
        Session.execute(sql_cmd)
    Session.commit()