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
Example #2
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()
Example #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
Example #4
0
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
Example #5
0
    def create(self):
        """
        POST /media: Create a new item

        With javascript/flash additional media can be uploaded individually
        """
        #user_log.debug("User is attempting to upload media:" + pprint.pformat(request.POST))
        form = request.POST
        if 'file_data' in form and 'content_id' in form and 'member_id' in form and 'key' in form:
            form_file = form["file_data"]
            content = get_content(int(form['content_id']))
            member = get_member(form['member_id'])
            if not member.check_action_key("attach to %d" % content.id,
                                           form['key']):
                return "invalid action key"
            if not content.editable_by(member):
                return "can't edit this article"
            media = Media()
            media.load_from_file(tmp_file=form_file,
                                 original_name=form_file.filename)
            content.attachments.append(media)
            Session.commit()
            user_log.info(
                "Media #%d appended to Content #%d using a key from Member %s"
                % (media.id, content.id, member.id))
            return "ok"
        else:
            return "missing file_data or content_id"
Example #6
0
def has_boomed(content, member):
    """
    Return boom_object or None for - has this member boomed this content
    """
    content = get_content(content)
    member = get_member(member)
    try:
        return Session.query(Boom).filter(Boom.member_id == member.id).filter(
            Boom.content_id == content.id).one()
    except:
        return None
Example #7
0
def profanity_check(content, url_base):
    """
    Checks content for profanity
    If there is a profanity
     - replace the content with the cleaned version
     - flag and list the removed words
     
    Todo idea? maybe we could profanity check drafts and tell users that the content has raised an issue before they publish it?
    """
    content = get_content(content)

    #if not content:
    #    log.warning("Content not found: %s" % str(content_id))
    #    return False
    assert content

    # Cydyne Profanity
    #profanity_response = civicboom.lib.services.cdyne_profanity.profanity_check(content.content)

    removed_words = []
    profanity_count = 0
    try:
        for field in ['title', 'content']:
            text_original = getattr(content, field)
            profanity_response = _profanity_check(text_original)
            if not profanity_response:
                raise ProfanityCheckFailedException()
            elif profanity_response['FoundProfanity']:
                text_clean = profanity_response['CleanText']
                setattr(content, field, text_clean)
                removed_words += get_diff_words(strip_html_tags(text_original),
                                                strip_html_tags(text_clean))
                profanity_count += profanity_response['ProfanityCount']
    except ProfanityCheckFailedException:
        log.debug("Profanity check failed")
        content.flag(
            comment=
            u"automatic profanity check failed, please manually inspect",
            url_base=url_base,
            moderator_address=config['email.moderator'])

    if profanity_count:
        log.debug("Profanitys found")
        content.flag(comment=u"found %d profanities: %s" %
                     (profanity_count, ','.join(removed_words)),
                     url_base=url_base,
                     moderator_address=config['email.moderator'])
    else:
        log.debug("No profanity found")

    return True
    def test_delete_content(self):

        self.assertEqual(self.num_content_public('delete_cascade'), 0)
        num_content_start = self.num_content_public()

        # Step 1: Create content
        self.content_id = self.create_content(type='assignment')

        # Step 2: Create responses, comments and accept
        self.log_in_as('unitfriend')
        #self.accept_assignment(self.content_id) # Accepting should now be done automatically when responding
        response_1_id = self.create_content(parent_id=self.content_id,
                                            title='response 1',
                                            content='delete_cascade')
        response_2_id = self.create_content(parent_id=self.content_id,
                                            title='response 2',
                                            content='delete_cascade')
        self.comment(self.content_id, 'delete_cascade comment')
        self.comment(response_1_id, 'delete_cascade response comment')

        response = self.app.get(url('content',
                                    id=self.content_id,
                                    format='json'),
                                status=200)
        response_json = json.loads(response.body)
        self.assertEqual(response_json['data']['responses']['count'], 2)
        self.assertEqual(response_json['data']['comments']['count'], 1)
        self.assertEqual(response_json['data']['accepted_status']['count'], 1)

        # Step 3: Delete content
        content_delete_cascade = get_content(self.content_id)
        content_delete_cascade.delete()

        # Step 4: Check deleted
        response = self.app.get(url('content', id=response_1_id,
                                    format='json'),
                                status=200)
        self.assertIn('delete_cascade response comment', response)
        response = self.app.get(url('content', id=response_2_id,
                                    format='json'),
                                status=200)

        self.assertEqual(
            Session.query(Content).filter_by(
                parent_id=self.content_id).count(), 0)
        self.assertEqual(
            Session.query(Tag).filter_by(name=u'delete_cascade').count(), 1
        )  #Tag remains at the end, this could be tidyed witha  delete orphan cascade
        self.assertEqual(
            Session.query(MemberAssignment).filter_by(
                content_id=self.content_id).count(), 0)
Example #9
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
Example #10
0
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
Example #11
0
 def _to_python(self, value, state):
     from pylons import tmpl_context as c
     from civicboom.lib.database.get_cached import get_content
     content = get_content(value)
     if not content:
         raise formencode.Invalid(self.message("not_content", state), value,
                                  state)
     if not content.viewable_by(c.logged_in_persona):
         raise formencode.Invalid(self.message("not_viewable", state),
                                  value, state)
     if self.persona_owner and content.creator.id != c.logged_in_persona.id:
         raise formencode.Invalid(self.message("not_owner", state), value,
                                  state)
     if self.content_type and content.__type__ != self.content_type:
         raise formencode.Invalid(
             self.message("not_type", state,
                          content_type=self.content_type), value, state)
     if self.return_object:
         return content
     return content.id
Example #12
0
 def set_due_date(assignment_id, due_date=None, timedelta=None):
     assignment = get_content(assignment_id)
     if not due_date:
         due_date = assignment.due_date - timedelta
     assignment.due_date = due_date
     Session.commit()
    def test_delete_user(self):

        self.assertEqual(self.num_members_public(username='******'), 0)
        self.assertEqual(self.num_content_public('delete_cascade'), 0)

        num_members_start = self.num_members_public()
        num_content_start = self.num_content_public()

        #-----------------------------------------------------------------------
        # Step 1: Create a set of interlinked objects over a range of tables to test delete cascades
        #-----------------------------------------------------------------------

        # Create user
        self.sign_up_as('delete_cascade')

        response = self.app.get(url('member',
                                    id='delete_cascade',
                                    format='json'),
                                status=200)
        response_json = json.loads(response.body)
        self.delete_cascade_member_id = response_json['data']['member']['id']
        self.assertNotEqual(self.delete_cascade_member_id, 0)

        self.assertEqual(self.num_members_public(), num_members_start + 1)

        # Setup known objects for delete cascade - Create a record in every related table to 'delete_cascade' member
        # when searching through the database after the deletion has occoured there should be NO trace of the string 'delete_cascade' anywhere!
        #  - follow
        #  - messages
        #  - content
        #  - group membership

        # Create content with media
        self.png1x1 = self.generate_image((1, 1))
        response = self.app.post(
            url('contents', format='json'),
            params={
                '_authentication_token': self.auth_token,
                'title': u'Testing delete_cascade',
                'contents': u'delete_cascade delete_cascade',
                'type': u'assignment',
                'tags_string': u'delete_cascade',
                #'submit_publish': u'publish' ,
            },
            upload_files=[("media_file", "1x1.png", self.png1x1)],
            status=201)
        response_json = json.loads(response.body)
        self.content_id = int(response_json['data']['id'])
        self.assertNotEqual(self.content_id, 0)
        response = self.app.get(url('content',
                                    id=self.content_id,
                                    format='json'),
                                status=200)
        response_json = json.loads(response.body)
        self.media_id = response_json['data']['content']['attachments'][0][
            'id']
        self.assertNotEqual(self.media_id, 0)

        # record current number of
        response = self.app.get(url('member', id='unittest', format='json'),
                                status=200)
        response_json = json.loads(response.body)
        num_following = response_json['data']['following']['count']
        num_followers = response_json['data']['followers']['count']
        num_boomed_content = response_json['data']['boomed']['count']
        #num_sent_messages =

        # Following, message recive, boom, comment
        self.log_in_as('unittest')
        self.follow('delete_cascade')
        self.send_member_message(
            'delete_cascade', 'testing delete_cascade',
            'this message should never be seen as it part of the delete_cascade test'
        )
        self.boom_content(self.content_id)
        self.comment(self.content_id, 'delete_cascade comment')
        unittest_assingment_id = self.create_content(type='assignment',
                                                     title='assignment',
                                                     content='assignment')

        # Follow, message send, comment
        self.log_in_as('delete_cascade')
        self.follow('unittest')
        self.send_member_message(
            'unittest', 'testing delete_cascade',
            'this message should never be seen as it part of the delete_cascade test'
        )
        self.comment(self.content_id, 'delete_cascade comment')
        # TODO: accept assignment by unittest
        self.accept_assignment(unittest_assingment_id)
        self.withdraw_assignment(unittest_assingment_id)

        response = self.app.get(url('member', id='unittest', format='json'),
                                status=200)
        response_json = json.loads(response.body)
        self.assertEqual(num_following + 1,
                         response_json['data']['following']['count'])
        self.assertEqual(num_followers + 1,
                         response_json['data']['followers']['count'])
        self.assertEqual(num_boomed_content + 1,
                         response_json['data']['boomed']['count'])

        # Create group - and therefor become a member
        self.group_id = self.create_group(
            username='******',
            name='Test group for delete_cascade',
            description=
            'This group should not be visible once the tests have completed because it will be removed',
            default_role='contributor',
            join_mode='public',
            member_visibility='public',
            default_content_visibility='public',
        )

        self.set_account_type(
            'plus')  # Create an account record associated with this user
        self.delete_cascade_payment_account_id = Session.query(
            Member).filter_by(id=self.delete_cascade_member_id).one(
            ).payment_account_id  # Get payment account id

        #-----------------------------------------------------------------------
        # Step 2: check data is in database
        #-----------------------------------------------------------------------
        # check tables deeply for all instances of the member id for removal

        self.assertEqual(
            self.num_members_public(username='******'), 1
        )  # 'delete_cascade' and 'delete_cascade_group' are both public members
        self.assertEqual(
            self.num_members_public(username='******'), 1)
        self.assertEqual(
            self.num_content_public('delete_cascade'), 1
        )  # 'delete_cascade' in public content # unittest assignment has delete cascade in title and content

        self.assertEqual(
            Session.query(Media).filter_by(id=self.media_id).count(), 1)
        self.assertEqual(
            Session.query(Content).filter_by(id=self.content_id).count(), 1)
        self.assertEqual(
            Session.query(Content).filter_by(
                parent_id=self.content_id).count(), 2)
        self.assertEqual(
            Session.query(Boom).filter_by(content_id=self.content_id).count(),
            1)

        self.assertEqual(
            Session.query(Member).filter_by(
                id=self.delete_cascade_member_id).count(), 1)
        #self.assertEqual(Session.query(Boom            ).filter_by(  member_id = self.delete_cascade_member_id).count(), 1) # The boom didnt come from the member 'delete_cascade'
        self.assertEqual(
            Session.query(GroupMembership).filter_by(
                member_id=self.delete_cascade_member_id).count(), 1)
        self.assertEqual(
            Session.query(Follow).filter_by(
                member_id=self.delete_cascade_member_id).count(), 1)
        self.assertEqual(
            Session.query(Follow).filter_by(
                follower_id=self.delete_cascade_member_id).count(), 1)
        self.assertEqual(
            Session.query(MemberAssignment).filter_by(
                member_id=self.delete_cascade_member_id).count(), 1)

        #self.assertEqual(Session.query(Message         ).filter_by(  target_id = self.delete_cascade_member_id).count() == 1) # cant, 1 as notifications are generated and it is more than 1
        self.assertEqual(
            Session.query(Message).filter_by(
                source_id=self.delete_cascade_member_id).count(), 1)

        self.assertEqual(
            Session.query(Tag).filter_by(name=u'delete_cascade').count(), 1)

        self.assertEqual(
            Session.query(Member).filter_by(
                payment_account_id=self.delete_cascade_payment_account_id).
            count(), 1)

        #-----------------------------------------------------------------------
        # Step 3: Delete objects
        #-----------------------------------------------------------------------

        member_delete_cascade = get_member('delete_cascade')
        member_delete_cascade.delete()

        group_delete_cascade = get_group('delete_cascade_group')
        self.assertEqual(group_delete_cascade.num_members, 0)
        group_delete_cascade.delete()

        #-----------------------------------------------------------------------
        # Step 4: Check for successful removal
        #-----------------------------------------------------------------------

        self.assertEqual(self.num_members_public(username='******'), 0)
        self.assertEqual(
            self.num_members_public(username='******'), 0)
        self.assertEqual(self.num_content_public('delete_cascade'), 0)
        self.assertEqual(num_members_start, self.num_members_public())
        self.assertEqual(num_content_start,
                         self.num_content_public() -
                         1)  # -1 because unittest set an assignment

        # check current number of
        response = self.app.get(url('member', id='unittest', format='json'),
                                status=200)
        response_json = json.loads(response.body)
        self.assertEqual(num_following,
                         response_json['data']['following']['count'])
        self.assertEqual(num_followers,
                         response_json['data']['followers']['count'])
        self.assertEqual(num_boomed_content,
                         response_json['data']['boomed']['count'])

        # check tables deeply for all instances of the member id for removal
        self.assertEqual(
            Session.query(Media).filter_by(id=self.media_id).count(), 0)
        self.assertEqual(
            Session.query(Content).filter_by(id=self.content_id).count(), 0)
        self.assertEqual(
            Session.query(Content).filter_by(
                parent_id=self.content_id).count(), 0)
        self.assertEqual(
            Session.query(Boom).filter_by(content_id=self.content_id).count(),
            0)

        self.assertEqual(
            Session.query(Member).filter_by(
                id=self.delete_cascade_member_id).count(), 0)
        self.assertEqual(
            Session.query(Boom).filter_by(
                member_id=self.delete_cascade_member_id).count(), 0)
        self.assertEqual(
            Session.query(GroupMembership).filter_by(
                member_id=self.delete_cascade_member_id).count(), 0)
        self.assertEqual(
            Session.query(Follow).filter_by(
                member_id=self.delete_cascade_member_id).count(), 0)
        self.assertEqual(
            Session.query(Follow).filter_by(
                follower_id=self.delete_cascade_member_id).count(), 0)
        self.assertEqual(
            Session.query(MemberAssignment).filter_by(
                member_id=self.delete_cascade_member_id).count(), 0)

        self.assertEqual(
            Session.query(Message).filter_by(
                target_id=self.delete_cascade_member_id).count(), 0)
        self.assertEqual(
            Session.query(Message).filter_by(
                source_id=self.delete_cascade_member_id).count(), 0)

        self.assertEqual(
            Session.query(Tag).filter_by(name=u'delete_cascade').count(), 1
        )  #Tag remains at the end, this could be tidyed witha  delete orphan cascade

        self.assertEqual(
            Session.query(Member).filter_by(
                payment_account_id=self.delete_cascade_payment_account_id).
            count(), 0)
        self.assertEqual(
            Session.query(PaymentAccount).filter_by(
                id=self.delete_cascade_payment_account_id).count(),
            1)  # The cascade dose not remove the payment account

        # Step 5: cleanup
        unittest_assignment = get_content(unittest_assingment_id)
        unittest_assignment.delete()
Example #14
0
def del_content(content):
    content = get_content(content)
    # TODO - AllanC - send notification to group members?
    #invalidate_content(content) # invalidate the cache - AllanC unneeded the event triggers handle this
    Session.delete(content)
    Session.commit()