Esempio n. 1
0
class NodeSerializer(JSONAPISerializer):
    # TODO: If we have to redo this implementation in any of the other serializers, subclass ChoiceField and make it
    # handle blank choices properly. Currently DRF ChoiceFields ignore blank options, which is incorrect in this
    # instance
    filterable_fields = frozenset([
        'id', 'title', 'description', 'public', 'tags', 'category',
        'date_created', 'date_modified', 'root', 'parent', 'contributors',
        'preprint'
    ])

    non_anonymized_fields = [
        'id', 'title', 'description', 'category', 'date_created',
        'date_modified', 'registration', 'tags', 'public', 'license', 'links',
        'children', 'comments', 'contributors', 'files', 'node_links',
        'parent', 'root', 'logs', 'wikis'
    ]

    id = IDField(source='_id', read_only=True)
    type = TypeField()

    category_choices = settings.NODE_CATEGORY_MAP.items()
    category_choices_string = ', '.join(
        ["'{}'".format(choice[0]) for choice in category_choices])

    title = ser.CharField(required=True)
    description = ser.CharField(required=False,
                                allow_blank=True,
                                allow_null=True)
    category = ser.ChoiceField(choices=category_choices,
                               help_text='Choices: ' + category_choices_string)
    date_created = ser.DateTimeField(read_only=True)
    date_modified = ser.DateTimeField(read_only=True)
    registration = ser.BooleanField(read_only=True, source='is_registration')
    preprint = ser.BooleanField(read_only=True, source='is_preprint')
    fork = ser.BooleanField(read_only=True, source='is_fork')
    collection = ser.BooleanField(read_only=True, source='is_collection')
    tags = JSONAPIListField(child=NodeTagField(), required=False)
    node_license = NodeLicenseSerializer(read_only=True, required=False)
    template_from = ser.CharField(
        required=False,
        allow_blank=False,
        allow_null=False,
        help_text=
        'Specify a node id for a node you would like to use as a template for the '
        'new node. Templating is like forking, except that you do not copy the '
        'files, only the project structure. Some information is changed on the top '
        'level project by submitting the appropriate fields in the request body, '
        'and some information will not change. By default, the description will '
        'be cleared and the project will be made private.')

    current_user_can_comment = ser.SerializerMethodField(
        help_text='Whether the current user is allowed to post comments')
    current_user_permissions = ser.SerializerMethodField(
        help_text='List of strings representing the permissions '
        'for the current user on this node.')

    # Public is only write-able by admins--see update method
    public = ser.BooleanField(
        source='is_public',
        required=False,
        help_text='Nodes that are made public will give read-only access '
        'to everyone. Private nodes require explicit read '
        'permission. Write and admin access are the same for '
        'public and private nodes. Administrators on a parent '
        'node have implicit read permissions for all child nodes')

    links = LinksField({'html': 'get_absolute_html_url'})
    # TODO: When we have osf_permissions.ADMIN permissions, make this writable for admins

    license = RelationshipField(
        related_view='licenses:license-detail',
        related_view_kwargs={'license_id': '<node_license.node_license._id>'},
    )

    children = RelationshipField(
        related_view='nodes:node-children',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_node_count'},
    )

    comments = RelationshipField(
        related_view='nodes:node-comments',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'unread': 'get_unread_comments_count'},
        filter={'target': '<pk>'})

    contributors = RelationshipField(
        related_view='nodes:node-contributors',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_contrib_count'},
    )

    files = RelationshipField(related_view='nodes:node-providers',
                              related_view_kwargs={'node_id': '<pk>'})

    wikis = RelationshipField(related_view='nodes:node-wikis',
                              related_view_kwargs={'node_id': '<pk>'})

    forked_from = RelationshipField(
        related_view=lambda n: 'registrations:registration-detail'
        if getattr(n, 'is_registration', False) else 'nodes:node-detail',
        related_view_kwargs={'node_id': '<forked_from_id>'})

    template_node = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<template_node._id>'})

    forks = RelationshipField(related_view='nodes:node-forks',
                              related_view_kwargs={'node_id': '<pk>'})

    node_links = RelationshipField(
        related_view='nodes:node-pointers',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_pointers_count'},
    )

    parent = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<parent_node._id>'},
        filter_key='parent_node')

    draft_registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-draft-registrations',
                          related_view_kwargs={'node_id': '<pk>'}))

    registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-registrations',
                          related_view_kwargs={'node_id': '<pk>'},
                          related_meta={'count': 'get_registration_count'}))

    affiliated_institutions = RelationshipField(
        related_view='nodes:node-institutions',
        related_view_kwargs={'node_id': '<pk>'},
        self_view='nodes:node-relationships-institutions',
        self_view_kwargs={'node_id': '<pk>'})

    root = RelationshipField(related_view='nodes:node-detail',
                             related_view_kwargs={'node_id': '<root._id>'})

    logs = RelationshipField(related_view='nodes:node-logs',
                             related_view_kwargs={'node_id': '<pk>'},
                             related_meta={'count': 'get_logs_count'})

    linked_nodes = RelationshipField(
        related_view='nodes:linked-nodes',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_node_links_count'},
        self_view='nodes:node-pointer-relationship',
        self_view_kwargs={'node_id': '<pk>'},
        self_meta={'count': 'get_node_links_count'})

    view_only_links = RelationshipField(
        related_view='nodes:node-view-only-links',
        related_view_kwargs={'node_id': '<pk>'},
    )

    def get_current_user_permissions(self, obj):
        user = self.context['request'].user
        if user.is_anonymous():
            return ['read']
        permissions = obj.get_permissions(user=user)
        if not permissions:
            permissions = ['read']
        return permissions

    def get_current_user_can_comment(self, obj):
        user = self.context['request'].user
        auth = Auth(user if not user.is_anonymous() else None)
        return obj.can_comment(auth)

    class Meta:
        type_ = 'nodes'

    def get_absolute_url(self, obj):
        return obj.get_absolute_url()

    # TODO: See if we can get the count filters into the filter rather than the serializer.

    def get_logs_count(self, obj):
        return len(obj.logs)

    def get_node_count(self, obj):
        auth = get_user_auth(self.context['request'])
        nodes = [
            node for node in obj.nodes
            if node.can_view(auth) and node.primary and not node.is_deleted
        ]
        return len(nodes)

    def get_contrib_count(self, obj):
        return len(obj.contributors)

    def get_registration_count(self, obj):
        auth = get_user_auth(self.context['request'])
        registrations = [
            node for node in obj.registrations_all if node.can_view(auth)
        ]
        return len(registrations)

    def get_pointers_count(self, obj):
        return len(obj.nodes_pointer)

    def get_node_links_count(self, obj):
        count = 0
        auth = get_user_auth(self.context['request'])
        for pointer in obj.nodes_pointer:
            if not pointer.node.is_deleted and not pointer.node.is_collection and pointer.node.can_view(
                    auth):
                count += 1
        return count

    def get_unread_comments_count(self, obj):
        user = get_user_auth(self.context['request']).user
        node_comments = Comment.find_n_unread(user=user, node=obj, page='node')

        return {'node': node_comments}

    def create(self, validated_data):
        request = self.context['request']
        user = request.user
        if 'template_from' in validated_data:
            template_from = validated_data.pop('template_from')
            template_node = Node.load(key=template_from)
            if template_node is None:
                raise exceptions.NotFound
            if not template_node.has_permission(
                    user, 'read', check_parent=False):
                raise exceptions.PermissionDenied

            validated_data.pop('creator')
            changed_data = {template_from: validated_data}
            node = template_node.use_as_template(auth=get_user_auth(request),
                                                 changes=changed_data)
        else:
            node = Node(**validated_data)
        try:
            node.save()
        except ValidationValueError as e:
            raise InvalidModelValueError(detail=e.message)
        if is_truthy(request.GET.get('inherit_contributors')
                     ) and validated_data['parent'].has_permission(
                         user, 'write'):
            auth = get_user_auth(request)
            parent = validated_data['parent']
            contributors = []
            for contributor in parent.contributors:
                if contributor is not user:
                    contributors.append({
                        'user':
                        contributor,
                        'permissions':
                        parent.get_permissions(contributor),
                        'visible':
                        parent.get_visible(contributor)
                    })
            node.add_contributors(contributors, auth=auth, log=True, save=True)
        return node

    def update(self, node, validated_data):
        """Update instance with the validated data. Requires
        the request to be in the serializer context.
        """
        assert isinstance(node, Node), 'node must be a Node'
        auth = get_user_auth(self.context['request'])
        old_tags = set([tag._id for tag in node.tags])
        if 'tags' in validated_data:
            current_tags = set(validated_data.pop('tags', []))
        elif self.partial:
            current_tags = set(old_tags)
        else:
            current_tags = set()

        for new_tag in (current_tags - old_tags):
            node.add_tag(new_tag, auth=auth)
        for deleted_tag in (old_tags - current_tags):
            node.remove_tag(deleted_tag, auth=auth)

        if validated_data:
            try:
                node.update(validated_data, auth=auth)
            except ValidationValueError as e:
                raise InvalidModelValueError(detail=e.message)
            except PermissionsError:
                raise exceptions.PermissionDenied
            except NodeUpdateError as e:
                raise exceptions.ValidationError(detail=e.reason)
            except NodeStateError as e:
                raise InvalidModelValueError(detail=e.message)

        return node
Esempio n. 2
0
class NodeSerializer(JSONAPISerializer):
    # TODO: If we have to redo this implementation in any of the other serializers, subclass ChoiceField and make it
    # handle blank choices properly. Currently DRF ChoiceFields ignore blank options, which is incorrect in this
    # instance
    filterable_fields = frozenset([
        'title',
        'description',
        'public',
        'tags',
        'category',
        'date_created',
        'date_modified',
        'registration'
    ])

    id = IDField(source='_id', read_only=True)
    type = TypeField()

    category_choices = Node.CATEGORY_MAP.keys()
    category_choices_string = ', '.join(["'{}'".format(choice) for choice in category_choices])

    title = ser.CharField(required=True)
    description = ser.CharField(required=False, allow_blank=True, allow_null=True)
    category = ser.ChoiceField(choices=category_choices, help_text="Choices: " + category_choices_string)
    date_created = ser.DateTimeField(read_only=True)
    date_modified = ser.DateTimeField(read_only=True)
    registration = ser.BooleanField(read_only=True, source='is_registration')
    fork = ser.BooleanField(read_only=True, source='is_fork')
    collection = DevOnly(ser.BooleanField(read_only=True, source='is_folder'))
    dashboard = ser.BooleanField(read_only=True, source='is_dashboard')
    tags = JSONAPIListField(child=NodeTagField(), required=False)

    # Public is only write-able by admins--see update method
    public = ser.BooleanField(source='is_public', required=False,
                              help_text='Nodes that are made public will give read-only access '
                                        'to everyone. Private nodes require explicit read '
                                        'permission. Write and admin access are the same for '
                                        'public and private nodes. Administrators on a parent '
                                        'node have implicit read permissions for all child nodes')

    links = LinksField({'html': 'get_absolute_url'})
    # TODO: When we have osf_permissions.ADMIN permissions, make this writable for admins

    children = RelationshipField(
        related_view='nodes:node-children',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_node_count'},
    )

    comments = RelationshipField(
        related_view='nodes:node-comments',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'unread': 'get_unread_comments_count'})

    contributors = RelationshipField(
        related_view='nodes:node-contributors',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_contrib_count'},
    )

    files = RelationshipField(
        related_view='nodes:node-providers',
        related_view_kwargs={'node_id': '<pk>'}
    )

    forked_from = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<forked_from_id>'}
    )

    node_links = DevOnly(RelationshipField(
        related_view='nodes:node-pointers',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_pointers_count'},
    ))

    parent = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<parent_id>'}
    )

    registrations = DevOnly(HideIfRegistration(RelationshipField(
        related_view='nodes:node-registrations',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_registration_count'}
    )))

    logs = RelationshipField(
        related_view='nodes:node-logs',
        related_view_kwargs={'node_id': '<pk>'},
    )

    class Meta:
        type_ = 'nodes'

    def get_absolute_url(self, obj):
        return obj.absolute_url

    # TODO: See if we can get the count filters into the filter rather than the serializer.

    def get_user_auth(self, request):
        user = request.user
        if user.is_anonymous():
            auth = Auth(None)
        else:
            auth = Auth(user)
        return auth

    def get_node_count(self, obj):
        auth = self.get_user_auth(self.context['request'])
        nodes = [node for node in obj.nodes if node.can_view(auth) and node.primary and not node.is_deleted]
        return len(nodes)

    def get_contrib_count(self, obj):
        return len(obj.contributors)

    def get_registration_count(self, obj):
        auth = self.get_user_auth(self.context['request'])
        registrations = [node for node in obj.node__registrations if node.can_view(auth)]
        return len(registrations)

    def get_pointers_count(self, obj):
        return len(obj.nodes_pointer)

    def get_unread_comments_count(self, obj):
        auth = self.get_user_auth(self.context['request'])
        user = auth.user
        return Comment.find_unread(user=user, node=obj)

    def create(self, validated_data):
        node = Node(**validated_data)
        try:
            node.save()
        except ValidationValueError as e:
            raise InvalidModelValueError(detail=e.message)
        return node

    def update(self, node, validated_data):
        """Update instance with the validated data. Requires
        the request to be in the serializer context.
        """
        assert isinstance(node, Node), 'node must be a Node'
        auth = self.get_user_auth(self.context['request'])
        old_tags = set([tag._id for tag in node.tags])
        if 'tags' in validated_data:
            current_tags = set(validated_data.get('tags'))
            del validated_data['tags']
        elif self.partial:
            current_tags = set(old_tags)
        else:
            current_tags = set()

        for new_tag in (current_tags - old_tags):
            node.add_tag(new_tag, auth=auth)
        for deleted_tag in (old_tags - current_tags):
            node.remove_tag(deleted_tag, auth=auth)

        if validated_data:
            try:
                node.update(validated_data, auth=auth)
            except ValidationValueError as e:
                raise InvalidModelValueError(detail=e.message)
            except PermissionsError:
                raise exceptions.PermissionDenied

        return node
Esempio n. 3
0
class RegistrationSerializer(NodeSerializer):
    admin_only_editable_fields = [
        'affiliated_institutions',
        'article_doi',
        'custom_citation',
        'description',
        'is_pending_retraction',
        'is_public',
        'license',
        'license_type',
        'subjects',
        'withdrawal_justification',
        'category',
    ]

    # Remember to add new RegistrationSerializer fields to this list
    # if you don't need them to be anonymized
    non_anonymized_fields = NodeSerializer.non_anonymized_fields + [
        'archiving',
        'article_doi',
        'date_registered',
        'date_withdrawn',
        'embargo_end_date',
        'embargoed',
        'pending_embargo_approval',
        'pending_embargo_termination_approval',
        'pending_registration_approval',
        'pending_withdrawal',
        'provider',
        'registered_by',
        'registered_from',
        'registered_meta',
        'registration_responses',
        'registration_schema',
        'registration_supplement',
        'withdrawal_justification',
        'withdrawn',
    ]

    title = ser.CharField(read_only=True)
    description = ser.CharField(required=False,
                                allow_blank=True,
                                allow_null=True)
    category_choices = NodeSerializer.category_choices
    category_choices_string = NodeSerializer.category_choices_string
    category = ser.ChoiceField(required=False,
                               choices=category_choices,
                               help_text='Choices: ' + category_choices_string)
    date_modified = VersionedDateTimeField(source='last_logged',
                                           read_only=True)
    fork = HideIfWithdrawal(ser.BooleanField(read_only=True, source='is_fork'))
    collection = HideIfWithdrawal(
        ser.BooleanField(read_only=True, source='is_collection'))
    access_requests_enabled = HideIfWithdrawal(
        ser.BooleanField(read_only=True))
    node_license = HideIfWithdrawal(
        NodeLicenseSerializer(required=False, source='license'))
    tags = HideIfWithdrawal(
        ValuesListField(attr_name='name',
                        child=ser.CharField(),
                        required=False))
    article_doi = ser.CharField(required=False, allow_null=True)
    public = HideIfWithdrawal(
        ser.BooleanField(
            source='is_public',
            required=False,
            help_text='Nodes that are made public will give read-only access '
            'to everyone. Private nodes require explicit read '
            'permission. Write and admin access are the same for '
            'public and private nodes. Administrators on a parent '
            'node have implicit read permissions for all child nodes',
        ))
    current_user_permissions = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='List of strings representing the permissions '
            'for the current user on this node.', ))

    pending_embargo_approval = HideIfWithdrawal(
        ser.BooleanField(
            read_only=True,
            source='is_pending_embargo',
            help_text=
            'The associated Embargo is awaiting approval by project admins.',
        ))
    pending_embargo_termination_approval = HideIfWithdrawal(
        ser.BooleanField(
            read_only=True,
            source='is_pending_embargo_termination',
            help_text=
            'The associated Embargo early termination is awaiting approval by project admins',
        ))
    embargoed = HideIfWithdrawal(
        ser.BooleanField(read_only=True, source='is_embargoed'))
    pending_registration_approval = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_registration',
            read_only=True,
            help_text=
            'The associated RegistrationApproval is awaiting approval by project admins.',
        ))
    archiving = HideIfWithdrawal(ser.BooleanField(read_only=True))
    pending_withdrawal = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_retraction',
            read_only=True,
            help_text=
            'The registration is awaiting withdrawal approval by project admins.',
        ))
    withdrawn = ser.BooleanField(
        source='is_retracted',
        read_only=True,
        help_text='The registration has been withdrawn.',
    )

    date_registered = VersionedDateTimeField(
        source='registered_date',
        read_only=True,
        help_text='Date time of registration.')
    date_withdrawn = VersionedDateTimeField(
        read_only=True,
        help_text='Date time of when this registration was retracted.')
    embargo_end_date = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='When the embargo on this registration will be lifted.'))
    custom_citation = HideIfWithdrawal(
        ser.CharField(allow_blank=True, required=False))

    withdrawal_justification = ser.CharField(read_only=True)
    template_from = HideIfWithdrawal(
        ser.CharField(
            read_only=True,
            allow_blank=False,
            allow_null=False,
            help_text=
            'Specify a node id for a node you would like to use as a template for the '
            'new node. Templating is like forking, except that you do not copy the '
            'files, only the project structure. Some information is changed on the top '
            'level project by submitting the appropriate fields in the request body, '
            'and some information will not change. By default, the description will '
            'be cleared and the project will be made private.',
        ))
    registration_supplement = ser.SerializerMethodField()
    # Will be deprecated in favor of registration_responses
    registered_meta = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text=
            'A dictionary with supplemental registration questions and responses.',
        ))
    registration_responses = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text=
            'A dictionary with supplemental registration questions and responses.',
        ))
    registered_by = HideIfWithdrawal(
        RelationshipField(
            related_view='users:user-detail',
            related_view_kwargs={'user_id': '<registered_user._id>'},
        ))

    registered_from = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<registered_from._id>'},
    )

    children = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-children',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_node_count'},
        ))

    comments = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-comments',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={
                'unread': 'get_unread_comments_count',
                'count': 'get_total_comments_count',
            },
            filter={'target': '<_id>'},
        ))

    contributors = RelationshipField(
        related_view='registrations:registration-contributors',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_contrib_count'},
    )

    bibliographic_contributors = RelationshipField(
        related_view='registrations:registration-bibliographic-contributors',
        related_view_kwargs={'node_id': '<_id>'},
    )

    implicit_contributors = RelationshipField(
        related_view='registrations:registration-implicit-contributors',
        related_view_kwargs={'node_id': '<_id>'},
        help_text=
        'This feature is experimental and being tested. It may be deprecated.',
    )

    files = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-storage-providers',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_files_count'},
        ))

    wikis = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-wikis',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_wiki_page_count'},
        ))

    forked_from = HideIfWithdrawal(
        RelationshipField(
            related_view=lambda n: 'registrations:registration-detail'
            if getattr(n, 'is_registration', False) else 'nodes:node-detail',
            related_view_kwargs={'node_id': '<forked_from_id>'},
        ))

    template_node = HideIfWithdrawal(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<template_node._id>'},
        ))

    license = HideIfWithdrawal(
        NodeLicenseRelationshipField(
            related_view='licenses:license-detail',
            related_view_kwargs={'license_id': '<license.node_license._id>'},
            read_only=False,
        ))

    logs = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-logs',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    forks = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-forks',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_forks_count'},
        ))

    groups = HideIfRegistration(
        RelationshipField(
            related_view='nodes:node-groups',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    node_links = ShowIfVersion(
        HideIfWithdrawal(
            RelationshipField(
                related_view='registrations:registration-pointers',
                related_view_kwargs={'node_id': '<_id>'},
                related_meta={'count': 'get_pointers_count'},
                help_text=
                'This feature is deprecated as of version 2.1. Use linked_nodes instead.',
            )),
        min_version='2.0',
        max_version='2.0',
    )

    linked_by_nodes = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-linked-by-nodes',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_linked_by_nodes_count'},
        ))

    linked_by_registrations = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-linked-by-registrations',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_linked_by_registrations_count'},
        ))

    parent = RelationshipField(
        related_view='registrations:registration-detail',
        related_view_kwargs={'node_id': '<parent_node._id>'},
        filter_key='parent_node',
    )

    root = RelationshipField(
        related_view='registrations:registration-detail',
        related_view_kwargs={'node_id': '<root._id>'},
    )

    region = HideIfWithdrawal(
        RelationshipField(
            related_view='regions:region-detail',
            related_view_kwargs={'region_id': '<osfstorage_region._id>'},
            read_only=True,
        ))

    affiliated_institutions = RelationshipField(
        related_view='registrations:registration-institutions',
        related_view_kwargs={'node_id': '<_id>'},
        self_view='registrations:registration-relationships-institutions',
        self_view_kwargs={'node_id': '<_id>'},
        read_only=False,
        many=True,
        required=False,
    )

    registration_schema = RelationshipField(
        related_view='schemas:registration-schema-detail',
        related_view_kwargs={'schema_id': '<registered_schema_id>'},
    )

    settings = HideIfRegistration(
        RelationshipField(
            related_view='nodes:node-settings',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    registrations = HideIfRegistration(
        RelationshipField(
            related_view='nodes:node-registrations',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    draft_registrations = HideIfRegistration(
        RelationshipField(
            related_view='nodes:node-draft-registrations',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    preprints = HideIfWithdrawal(
        HideIfRegistration(
            RelationshipField(
                related_view='nodes:node-preprints',
                related_view_kwargs={'node_id': '<_id>'},
            )))

    identifiers = RelationshipField(
        related_view='registrations:identifier-list',
        related_view_kwargs={'node_id': '<_id>'},
    )

    linked_nodes = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:linked-nodes',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_node_links_count'},
            self_view='registrations:node-pointer-relationship',
            self_view_kwargs={'node_id': '<_id>'},
        ))

    linked_registrations = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:linked-registrations',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_registration_links_count'},
            self_view='registrations:node-registration-pointer-relationship',
            self_view_kwargs={'node_id': '<_id>'},
        ))

    view_only_links = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-view-only-links',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_view_only_links_count'},
        ))

    citation = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-citation',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    provider = RegistrationProviderRelationshipField(
        related_view=
        'providers:registration-providers:registration-provider-detail',
        related_view_kwargs={'provider_id': '<provider._id>'},
        read_only=True,
    )

    @property
    def subjects_related_view(self):
        # Overrides TaxonomizableSerializerMixin
        return 'registrations:registration-subjects'

    @property
    def subjects_self_view(self):
        # Overrides TaxonomizableSerializerMixin
        return 'registrations:registration-relationships-subjects'

    links = LinksField({'html': 'get_absolute_html_url'})

    def get_absolute_url(self, obj):
        return obj.get_absolute_url()

    def get_registered_meta(self, obj):
        if obj.registered_meta:
            meta_values = self.anonymize_registered_meta(obj)
            try:
                return json.loads(meta_values)
            except TypeError:
                return meta_values
            except ValueError:
                return meta_values
        return None

    def get_registration_responses(self, obj):
        if obj.registration_responses:
            return self.anonymize_registration_responses(obj)
        return None

    def get_embargo_end_date(self, obj):
        if obj.embargo_end_date:
            return obj.embargo_end_date
        return None

    def get_registration_supplement(self, obj):
        if obj.registered_schema:
            schema = obj.registered_schema.first()
            if schema is None:
                return None
            return schema.name
        return None

    def get_current_user_permissions(self, obj):
        return NodeSerializer.get_current_user_permissions(self, obj)

    def get_view_only_links_count(self, obj):
        return obj.private_links.filter(is_deleted=False).count()

    def get_total_comments_count(self, obj):
        return obj.comment_set.filter(page='node', is_deleted=False).count()

    def get_files_count(self, obj):
        return obj.files_count or 0

    def anonymize_registered_meta(self, obj):
        """
        Looks at every question on every page of the schema, for any titles
        that have a contributor-input block type.  If present, deletes that question's response
        from meta_values.
        """
        cleaned_registered_meta = strip_registered_meta_comments(
            obj.registered_meta.values()[0])
        return self.anonymize_fields(obj, cleaned_registered_meta)

    def anonymize_registration_responses(self, obj):
        """
        For any questions that have a `contributor-input` block type, delete
        that question's response from registration_responses.

        We want to make sure author's names that need to be anonymized
        aren't surfaced when viewed through an anonymous VOL
        """
        return self.anonymize_fields(obj, obj.registration_responses)

    def anonymize_fields(self, obj, data):
        """
        Consolidates logic to anonymize fields with contributor information
        on both registered_meta and registration_responses
        """
        if is_anonymized(self.context['request']):
            anonymous_registration_response_keys = obj.get_contributor_registration_response_keys(
            )

            for key in anonymous_registration_response_keys:
                if key in data:
                    del data[key]

        return data

    def check_admin_perms(self, registration, user, validated_data):
        """
        While admin/write users can make both make modifications to registrations,
        most fields are restricted to admin-only edits.  You must be an admin
        contributor on the registration; you cannot have gotten your admin
        permissions through group membership.

        Add fields that need admin perms to admin_only_editable_fields
        """
        user_is_admin = registration.is_admin_contributor(user)
        for field in validated_data:
            if field in self.admin_only_editable_fields and not user_is_admin:
                raise exceptions.PermissionDenied()

    def update_registration_tags(self, registration, validated_data, auth):
        new_tags = validated_data.pop('tags', [])
        try:
            registration.update_tags(new_tags, auth=auth)
        except NodeStateError as err:
            raise Conflict(str(err))

    def retract_registration(self, registration, validated_data, user):
        is_pending_retraction = validated_data.pop('is_pending_retraction',
                                                   None)
        withdrawal_justification = validated_data.pop(
            'withdrawal_justification', None)
        if withdrawal_justification and not is_pending_retraction:
            raise exceptions.ValidationError(
                'You cannot provide a withdrawal_justification without a concurrent withdrawal request.',
            )
        if is_truthy(is_pending_retraction):
            if registration.is_pending_retraction:
                raise exceptions.ValidationError(
                    'This registration is already pending withdrawal.')
            try:
                retraction = registration.retract_registration(
                    user, withdrawal_justification, save=True)
            except NodeStateError as err:
                raise exceptions.ValidationError(str(err))
            retraction.ask(
                registration.get_active_contributors_recursive(
                    unique_users=True))
        elif is_pending_retraction is not None:
            raise exceptions.ValidationError(
                'You cannot set is_pending_withdrawal to False.')

    def update(self, registration, validated_data):
        user = self.context['request'].user
        auth = Auth(user)
        self.check_admin_perms(registration, user, validated_data)
        validated_data.pop('_id', None)

        if 'tags' in validated_data:
            self.update_registration_tags(registration, validated_data, auth)
        if 'custom_citation' in validated_data:
            registration.update_custom_citation(
                validated_data.pop('custom_citation'), auth)
        if 'license_type' in validated_data or 'license' in validated_data:
            license_details = get_license_details(registration, validated_data)
            validated_data['node_license'] = license_details
            validated_data.pop('license_type', None)
            validated_data.pop('license', None)
        if 'affiliated_institutions' in validated_data:
            institutions_list = validated_data.pop('affiliated_institutions')
            new_institutions = [{
                '_id': institution
            } for institution in institutions_list]
            update_institutions(registration, new_institutions, user)
            registration.save()
        if 'subjects' in validated_data:
            subjects = validated_data.pop('subjects', None)
            self.update_subjects(registration, subjects, auth)
        if 'withdrawal_justification' in validated_data or 'is_pending_retraction' in validated_data:
            self.retract_registration(registration, validated_data, user)
        if 'is_public' in validated_data:
            if validated_data.get('is_public') is False:
                raise exceptions.ValidationError(
                    'Registrations can only be turned from private to public.')

        try:
            registration.update(validated_data, auth=auth)
        except ValidationError as e:
            raise InvalidModelValueError(detail=e.messages[0])
        except NodeUpdateError as err:
            raise exceptions.ValidationError(err.reason)
        except NodeStateError as err:
            raise exceptions.ValidationError(str(err))

        return registration

    class Meta:
        type_ = 'registrations'
Esempio n. 4
0
class BaseRegistrationSerializer(NodeSerializer):

    title = ser.CharField(read_only=True)
    description = ser.CharField(read_only=True)
    category_choices = NodeSerializer.category_choices
    category_choices_string = NodeSerializer.category_choices_string
    category = HideIfWithdrawal(
        ser.ChoiceField(read_only=True,
                        choices=category_choices,
                        help_text='Choices: ' + category_choices_string))
    date_modified = VersionedDateTimeField(source='last_logged',
                                           read_only=True)
    fork = HideIfWithdrawal(ser.BooleanField(read_only=True, source='is_fork'))
    collection = HideIfWithdrawal(
        ser.BooleanField(read_only=True, source='is_collection'))
    access_requests_enabled = HideIfWithdrawal(
        ser.BooleanField(read_only=True))
    node_license = HideIfWithdrawal(NodeLicenseSerializer(read_only=True))
    tags = HideIfWithdrawal(
        ValuesListField(attr_name='name',
                        child=ser.CharField(),
                        required=False))
    public = HideIfWithdrawal(
        ser.BooleanField(
            source='is_public',
            required=False,
            help_text='Nodes that are made public will give read-only access '
            'to everyone. Private nodes require explicit read '
            'permission. Write and admin access are the same for '
            'public and private nodes. Administrators on a parent '
            'node have implicit read permissions for all child nodes'))
    current_user_permissions = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='List of strings representing the permissions '
            'for the current user on this node.'))

    pending_embargo_approval = HideIfWithdrawal(
        ser.BooleanField(
            read_only=True,
            source='is_pending_embargo',
            help_text=
            'The associated Embargo is awaiting approval by project admins.'))
    pending_registration_approval = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_registration',
            read_only=True,
            help_text=
            'The associated RegistrationApproval is awaiting approval by project admins.'
        ))
    pending_withdrawal = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_retraction',
            read_only=True,
            help_text=
            'The registration is awaiting withdrawal approval by project admins.'
        ))
    withdrawn = ser.BooleanField(
        source='is_retracted',
        read_only=True,
        help_text='The registration has been withdrawn.')

    date_registered = VersionedDateTimeField(
        source='registered_date',
        read_only=True,
        help_text='Date time of registration.')
    date_withdrawn = VersionedDateTimeField(
        source='retraction.date_retracted',
        read_only=True,
        help_text='Date time of when this registration was retracted.')
    embargo_end_date = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='When the embargo on this registration will be lifted.'))

    withdrawal_justification = ser.CharField(source='retraction.justification',
                                             read_only=True)
    template_from = HideIfWithdrawal(
        ser.CharField(
            read_only=True,
            allow_blank=False,
            allow_null=False,
            help_text=
            'Specify a node id for a node you would like to use as a template for the '
            'new node. Templating is like forking, except that you do not copy the '
            'files, only the project structure. Some information is changed on the top '
            'level project by submitting the appropriate fields in the request body, '
            'and some information will not change. By default, the description will '
            'be cleared and the project will be made private.'))
    registration_supplement = ser.SerializerMethodField()
    registered_meta = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text=
            'A dictionary with supplemental registration questions and responses.'
        ))

    registered_by = HideIfWithdrawal(
        RelationshipField(
            related_view='users:user-detail',
            related_view_kwargs={'user_id': '<registered_user._id>'}))

    registered_from = HideIfWithdrawal(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<registered_from._id>'}))

    children = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-children',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_node_count'},
        ))

    comments = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-comments',
                          related_view_kwargs={'node_id': '<_id>'},
                          related_meta={'unread': 'get_unread_comments_count'},
                          filter={'target': '<_id>'}))

    contributors = RelationshipField(
        related_view='registrations:registration-contributors',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_contrib_count'})

    implicit_contributors = RelationshipField(
        related_view='registrations:registration-implicit-contributors',
        related_view_kwargs={'node_id': '<_id>'},
        help_text=
        'This feature is experimental and being tested. It may be deprecated.')

    files = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-providers',
                          related_view_kwargs={'node_id': '<_id>'}))

    wikis = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-wikis',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    forked_from = HideIfWithdrawal(
        RelationshipField(
            related_view=lambda n: 'registrations:registration-detail'
            if getattr(n, 'is_registration', False) else 'nodes:node-detail',
            related_view_kwargs={'node_id': '<forked_from_id>'}))

    template_node = HideIfWithdrawal(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<template_node._id>'}))

    license = HideIfWithdrawal(
        RelationshipField(
            related_view='licenses:license-detail',
            related_view_kwargs={
                'license_id': '<node_license.node_license._id>'
            },
        ))

    logs = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-logs',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    forks = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-forks',
                          related_view_kwargs={'node_id': '<_id>'}))

    node_links = ShowIfVersion(HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-pointers',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_pointers_count'},
            help_text=
            'This feature is deprecated as of version 2.1. Use linked_nodes instead.'
        )),
                               min_version='2.0',
                               max_version='2.0')

    parent = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<parent_node._id>'},
                          filter_key='parent_node'))

    root = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<root._id>'}))

    affiliated_institutions = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-institutions',
            related_view_kwargs={'node_id': '<_id>'}))

    registration_schema = RelationshipField(
        related_view='metaschemas:registration-metaschema-detail',
        related_view_kwargs={'metaschema_id': '<registered_schema_id>'})

    registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-registrations',
                          related_view_kwargs={'node_id': '<_id>'}))

    draft_registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-draft-registrations',
                          related_view_kwargs={'node_id': '<_id>'}))

    preprints = HideIfWithdrawal(
        HideIfRegistration(
            RelationshipField(related_view='nodes:node-preprints',
                              related_view_kwargs={'node_id': '<_id>'})))

    identifiers = HideIfWithdrawal(
        RelationshipField(related_view='registrations:identifier-list',
                          related_view_kwargs={'node_id': '<_id>'}))

    linked_nodes = HideIfWithdrawal(
        RelationshipField(related_view='registrations:linked-nodes',
                          related_view_kwargs={'node_id': '<_id>'},
                          related_meta={'count': 'get_node_links_count'},
                          self_view='registrations:node-pointer-relationship',
                          self_view_kwargs={'node_id': '<_id>'}))

    linked_registrations = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:linked-registrations',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_registration_links_count'},
            self_view='registrations:node-registration-pointer-relationship',
            self_view_kwargs={'node_id': '<_id>'}))

    view_only_links = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-view-only-links',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_view_only_links_count'},
        ))

    citation = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-citation',
                          related_view_kwargs={'node_id': '<_id>'}))

    links = LinksField({
        'self': 'get_registration_url',
        'html': 'get_absolute_html_url'
    })

    def get_registration_url(self, obj):
        return absolute_reverse(
            'registrations:registration-detail',
            kwargs={
                'node_id':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

    def get_absolute_url(self, obj):
        return self.get_registration_url(obj)

    def create(self, validated_data):
        auth = get_user_auth(self.context['request'])
        draft = validated_data.pop('draft')
        registration_choice = validated_data.pop('registration_choice',
                                                 'immediate')
        embargo_lifted = validated_data.pop('lift_embargo', None)
        reviewer = is_prereg_admin_not_project_admin(self.context['request'],
                                                     draft)

        try:
            draft.validate_metadata(metadata=draft.registration_metadata,
                                    reviewer=reviewer,
                                    required_fields=True)
        except ValidationValueError as e:
            raise exceptions.ValidationError(e.message)

        registration = draft.register(auth, save=True)

        if registration_choice == 'embargo':
            if not embargo_lifted:
                raise exceptions.ValidationError(
                    'lift_embargo must be specified.')
            embargo_end_date = embargo_lifted.replace(tzinfo=pytz.utc)
            try:
                registration.embargo_registration(auth.user, embargo_end_date)
            except ValidationError as err:
                raise exceptions.ValidationError(err.message)
        else:
            try:
                registration.require_approval(auth.user)
            except NodeStateError as err:
                raise exceptions.ValidationError(err)

        registration.save()
        return registration

    def get_registered_meta(self, obj):
        if obj.registered_meta:
            meta_values = obj.registered_meta.values()[0]
            try:
                return json.loads(meta_values)
            except TypeError:
                return meta_values
            except ValueError:
                return meta_values
        return None

    def get_embargo_end_date(self, obj):
        if obj.embargo_end_date:
            return obj.embargo_end_date
        return None

    def get_registration_supplement(self, obj):
        if obj.registered_schema:
            schema = obj.registered_schema.first()
            if schema is None:
                return None
            return schema.name
        return None

    def get_current_user_permissions(self, obj):
        return NodeSerializer.get_current_user_permissions(self, obj)

    def update(self, registration, validated_data):
        auth = Auth(self.context['request'].user)
        # Update tags
        if 'tags' in validated_data:
            new_tags = validated_data.pop('tags', [])
            try:
                registration.update_tags(new_tags, auth=auth)
            except NodeStateError as err:
                raise Conflict(err.message)

        is_public = validated_data.get('is_public', None)
        if is_public is not None:
            if is_public:
                try:
                    registration.update(validated_data, auth=auth)
                except NodeUpdateError as err:
                    raise exceptions.ValidationError(err.reason)
                except NodeStateError as err:
                    raise exceptions.ValidationError(err.message)
            else:
                raise exceptions.ValidationError(
                    'Registrations can only be turned from private to public.')
        return registration

    class Meta:
        type_ = 'registrations'
Esempio n. 5
0
class NodeSerializer(JSONAPISerializer):
    # TODO: If we have to redo this implementation in any of the other serializers, subclass ChoiceField and make it
    # handle blank choices properly. Currently DRF ChoiceFields ignore blank options, which is incorrect in this
    # instance
    filterable_fields = frozenset([
        'id', 'title', 'description', 'public', 'tags', 'category',
        'date_created', 'date_modified', 'root', 'parent', 'contributors',
        'preprint'
    ])

    non_anonymized_fields = [
        'id', 'title', 'description', 'category', 'date_created',
        'date_modified', 'registration', 'tags', 'public', 'license', 'links',
        'children', 'comments', 'contributors', 'files', 'node_links',
        'parent', 'root', 'logs', 'wikis'
    ]

    id = IDField(source='_id', read_only=True)
    type = TypeField()

    category_choices = settings.NODE_CATEGORY_MAP.items()
    category_choices_string = ', '.join(
        ["'{}'".format(choice[0]) for choice in category_choices])

    title = ser.CharField(required=True)
    description = ser.CharField(required=False,
                                allow_blank=True,
                                allow_null=True)
    category = ser.ChoiceField(choices=category_choices,
                               help_text='Choices: ' + category_choices_string)
    date_created = DateByVersion(source='created', read_only=True)
    date_modified = DateByVersion(source='last_logged', read_only=True)
    registration = ser.BooleanField(read_only=True, source='is_registration')
    preprint = ser.BooleanField(read_only=True, source='is_preprint')
    fork = ser.BooleanField(read_only=True, source='is_fork')
    collection = ser.BooleanField(read_only=True, source='is_collection')
    tags = JSONAPIListField(child=NodeTagField(), required=False)
    node_license = NodeLicenseSerializer(required=False, source='license')
    template_from = ser.CharField(
        required=False,
        allow_blank=False,
        allow_null=False,
        help_text=
        'Specify a node id for a node you would like to use as a template for the '
        'new node. Templating is like forking, except that you do not copy the '
        'files, only the project structure. Some information is changed on the top '
        'level project by submitting the appropriate fields in the request body, '
        'and some information will not change. By default, the description will '
        'be cleared and the project will be made private.')

    current_user_can_comment = ser.SerializerMethodField(
        help_text='Whether the current user is allowed to post comments')
    current_user_permissions = ser.SerializerMethodField(
        help_text='List of strings representing the permissions '
        'for the current user on this node.')

    # Public is only write-able by admins--see update method
    public = ser.BooleanField(
        source='is_public',
        required=False,
        help_text='Nodes that are made public will give read-only access '
        'to everyone. Private nodes require explicit read '
        'permission. Write and admin access are the same for '
        'public and private nodes. Administrators on a parent '
        'node have implicit read permissions for all child nodes')

    links = LinksField({'html': 'get_absolute_html_url'})
    # TODO: When we have osf_permissions.ADMIN permissions, make this writable for admins

    license = NodeLicenseRelationshipField(
        related_view='licenses:license-detail',
        related_view_kwargs={'license_id': '<license.node_license._id>'},
        read_only=False)

    children = RelationshipField(
        related_view='nodes:node-children',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_node_count'},
    )

    comments = RelationshipField(
        related_view='nodes:node-comments',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'unread': 'get_unread_comments_count'},
        filter={'target': '<_id>'})

    contributors = RelationshipField(
        related_view='nodes:node-contributors',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_contrib_count'},
    )

    files = RelationshipField(related_view='nodes:node-providers',
                              related_view_kwargs={'node_id': '<_id>'})

    wikis = RelationshipField(related_view='nodes:node-wikis',
                              related_view_kwargs={'node_id': '<_id>'})

    forked_from = RelationshipField(
        related_view=lambda n: 'registrations:registration-detail'
        if getattr(n, 'is_registration', False) else 'nodes:node-detail',
        related_view_kwargs={'node_id': '<forked_from_guid>'})

    template_node = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<template_node._id>'})

    forks = RelationshipField(related_view='nodes:node-forks',
                              related_view_kwargs={'node_id': '<_id>'})

    node_links = ShowIfVersion(RelationshipField(
        related_view='nodes:node-pointers',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_pointers_count'},
        help_text=
        'This feature is deprecated as of version 2.1. Use linked_nodes instead.'
    ),
                               min_version='2.0',
                               max_version='2.0')

    parent = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<parent_node._id>'},
        filter_key='parent_node')

    identifiers = RelationshipField(related_view='nodes:identifier-list',
                                    related_view_kwargs={'node_id': '<_id>'})

    draft_registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-draft-registrations',
                          related_view_kwargs={'node_id': '<_id>'}))

    registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-registrations',
                          related_view_kwargs={'node_id': '<_id>'},
                          related_meta={'count': 'get_registration_count'}))

    affiliated_institutions = RelationshipField(
        related_view='nodes:node-institutions',
        related_view_kwargs={'node_id': '<_id>'},
        self_view='nodes:node-relationships-institutions',
        self_view_kwargs={'node_id': '<_id>'})

    root = RelationshipField(related_view='nodes:node-detail',
                             related_view_kwargs={'node_id': '<root._id>'})

    logs = RelationshipField(related_view='nodes:node-logs',
                             related_view_kwargs={'node_id': '<_id>'},
                             related_meta={'count': 'get_logs_count'})

    linked_nodes = RelationshipField(
        related_view='nodes:linked-nodes',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_node_links_count'},
        self_view='nodes:node-pointer-relationship',
        self_view_kwargs={'node_id': '<_id>'},
        self_meta={'count': 'get_node_links_count'})

    linked_registrations = RelationshipField(
        related_view='nodes:linked-registrations',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_registration_links_count'},
        self_view='nodes:node-registration-pointer-relationship',
        self_view_kwargs={'node_id': '<_id>'},
        self_meta={'count': 'get_node_links_count'})

    view_only_links = RelationshipField(
        related_view='nodes:node-view-only-links',
        related_view_kwargs={'node_id': '<_id>'},
    )

    citation = RelationshipField(related_view='nodes:node-citation',
                                 related_view_kwargs={'node_id': '<_id>'})

    preprints = HideIfRegistration(
        RelationshipField(related_view='nodes:node-preprints',
                          related_view_kwargs={'node_id': '<_id>'}))

    def get_current_user_permissions(self, obj):
        user = self.context['request'].user
        if user.is_anonymous:
            return ['read']
        permissions = obj.get_permissions(user=user)
        if not permissions:
            permissions = ['read']
        return permissions

    def get_current_user_can_comment(self, obj):
        user = self.context['request'].user
        auth = Auth(user if not user.is_anonymous else None)
        return obj.can_comment(auth)

    class Meta:
        type_ = 'nodes'

    def get_absolute_url(self, obj):
        return obj.get_absolute_url()

    # TODO: See if we can get the count filters into the filter rather than the serializer.

    def get_logs_count(self, obj):
        return obj.logs.count()

    def get_node_count(self, obj):
        auth = get_user_auth(self.context['request'])
        user_id = getattr(auth.user, 'id', None)
        with connection.cursor() as cursor:
            cursor.execute(
                '''
                WITH RECURSIVE parents AS (
                  SELECT parent_id, child_id
                  FROM osf_noderelation
                  WHERE child_id = %s AND is_node_link IS FALSE
                UNION ALL
                  SELECT osf_noderelation.parent_id, parents.parent_id AS child_id
                  FROM parents JOIN osf_noderelation ON parents.PARENT_ID = osf_noderelation.child_id
                  WHERE osf_noderelation.is_node_link IS FALSE
                ), has_admin AS (SELECT * FROM osf_contributor WHERE (node_id IN (SELECT parent_id FROM parents) OR node_id = %s) AND user_id = %s AND admin IS TRUE LIMIT 1)
                SELECT DISTINCT
                  COUNT(child_id)
                FROM
                  osf_noderelation
                JOIN osf_abstractnode ON osf_noderelation.child_id = osf_abstractnode.id
                JOIN osf_contributor ON osf_abstractnode.id = osf_contributor.node_id
                LEFT JOIN osf_privatelink_nodes ON osf_abstractnode.id = osf_privatelink_nodes.abstractnode_id
                LEFT JOIN osf_privatelink ON osf_privatelink_nodes.privatelink_id = osf_privatelink.id
                WHERE parent_id = %s AND is_node_link IS FALSE
                AND osf_abstractnode.is_deleted IS FALSE
                AND (
                  osf_abstractnode.is_public
                  OR (TRUE IN (SELECT TRUE FROM has_admin))
                  OR (osf_contributor.user_id = %s AND osf_contributor.read IS TRUE)
                  OR (osf_privatelink.key = %s AND osf_privatelink.is_deleted = FALSE)
                );
            ''', [obj.id, obj.id, user_id, obj.id, user_id, auth.private_key])

            return int(cursor.fetchone()[0])

    def get_contrib_count(self, obj):
        return len(obj.contributors)

    def get_registration_count(self, obj):
        auth = get_user_auth(self.context['request'])
        registrations = [
            node for node in obj.registrations_all if node.can_view(auth)
        ]
        return len(registrations)

    def get_pointers_count(self, obj):
        return obj.linked_nodes.count()

    def get_node_links_count(self, obj):
        count = 0
        auth = get_user_auth(self.context['request'])
        for pointer in obj.linked_nodes.filter(is_deleted=False).exclude(
                type='osf.collection').exclude(type='osf.registration'):
            if pointer.can_view(auth):
                count += 1
        return count

    def get_registration_links_count(self, obj):
        count = 0
        auth = get_user_auth(self.context['request'])
        for pointer in obj.linked_nodes.filter(
                is_deleted=False,
                type='osf.registration').exclude(type='osf.collection'):
            if pointer.can_view(auth):
                count += 1
        return count

    def get_unread_comments_count(self, obj):
        user = get_user_auth(self.context['request']).user
        node_comments = Comment.find_n_unread(user=user, node=obj, page='node')

        return {'node': node_comments}

    def create(self, validated_data):
        request = self.context['request']
        user = request.user
        Node = apps.get_model('osf.Node')
        tag_instances = []
        if 'tags' in validated_data:
            tags = validated_data.pop('tags')
            for tag in tags:
                tag_instance, created = Tag.objects.get_or_create(
                    name=tag, defaults=dict(system=False))
                tag_instances.append(tag_instance)
        if 'template_from' in validated_data:
            template_from = validated_data.pop('template_from')
            template_node = Node.load(template_from)
            if template_node is None:
                raise exceptions.NotFound
            if not template_node.has_permission(
                    user, 'read', check_parent=False):
                raise exceptions.PermissionDenied

            validated_data.pop('creator')
            changed_data = {template_from: validated_data}
            node = template_node.use_as_template(auth=get_user_auth(request),
                                                 changes=changed_data)
        else:
            node = Node(**validated_data)
        try:
            node.save()
        except ValidationError as e:
            raise InvalidModelValueError(detail=e.messages[0])
        if len(tag_instances):
            for tag in tag_instances:
                node.tags.add(tag)
        if is_truthy(request.GET.get('inherit_contributors')
                     ) and validated_data['parent'].has_permission(
                         user, 'write'):
            auth = get_user_auth(request)
            parent = validated_data['parent']
            contributors = []
            for contributor in parent.contributor_set.exclude(user=user):
                contributors.append({
                    'user':
                    contributor.user,
                    'permissions':
                    parent.get_permissions(contributor.user),
                    'visible':
                    contributor.visible
                })
                if not contributor.user.is_registered:
                    node.add_unregistered_contributor(
                        fullname=contributor.user.fullname,
                        email=contributor.user.email,
                        auth=auth,
                        permissions=parent.get_permissions(contributor.user),
                        existing_user=contributor.user)
            node.add_contributors(contributors, auth=auth, log=True, save=True)
        return node

    def update(self, node, validated_data):
        """Update instance with the validated data. Requires
        the request to be in the serializer context.
        """
        assert isinstance(node, AbstractNode), 'node must be a Node'
        auth = get_user_auth(self.context['request'])

        # Update tags
        if 'tags' in validated_data:
            new_tags = set(validated_data.pop('tags', []))
            node.update_tags(new_tags, auth=auth)

        if validated_data:

            if 'license_type' in validated_data or 'license' in validated_data:
                license_details = get_license_details(node, validated_data)
                validated_data['node_license'] = license_details

            try:
                node.update(validated_data, auth=auth)
            except ValidationError as e:
                raise InvalidModelValueError(detail=e.message)
            except PermissionsError:
                raise exceptions.PermissionDenied
            except NodeUpdateError as e:
                raise exceptions.ValidationError(detail=e.reason)
            except NodeStateError as e:
                raise InvalidModelValueError(detail=e.message)

        return node
Esempio n. 6
0
class RegistrationSerializer(NodeSerializer):

    category_choices = NodeSerializer.category_choices
    category_choices_string = NodeSerializer.category_choices_string
    category = HideIfWithdrawal(
        ser.ChoiceField(choices=category_choices,
                        help_text="Choices: " + category_choices_string))

    date_modified = HideIfWithdrawal(ser.DateTimeField(read_only=True))
    fork = HideIfWithdrawal(ser.BooleanField(read_only=True, source='is_fork'))
    collection = HideIfWithdrawal(
        ser.BooleanField(read_only=True, source='is_collection'))
    node_license = HideIfWithdrawal(NodeLicenseSerializer())
    tags = HideIfWithdrawal(
        JSONAPIListField(child=NodeTagField(), required=False))
    public = HideIfWithdrawal(
        ser.BooleanField(
            source='is_public',
            required=False,
            help_text='Nodes that are made public will give read-only access '
            'to everyone. Private nodes require explicit read '
            'permission. Write and admin access are the same for '
            'public and private nodes. Administrators on a parent '
            'node have implicit read permissions for all child nodes'))
    current_user_permissions = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='List of strings representing the permissions '
            'for the current user on this node.'))

    pending_embargo_approval = HideIfWithdrawal(
        ser.BooleanField(
            read_only=True,
            source='is_pending_embargo',
            help_text=
            'The associated Embargo is awaiting approval by project admins.'))
    pending_registration_approval = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_registration',
            read_only=True,
            help_text=
            'The associated RegistrationApproval is awaiting approval by project admins.'
        ))
    pending_withdrawal = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_retraction',
            read_only=True,
            help_text=
            'The registration is awaiting withdrawal approval by project admins.'
        ))
    withdrawn = ser.BooleanField(
        source='is_retracted',
        read_only=True,
        help_text='The registration has been withdrawn.')

    date_registered = ser.DateTimeField(source='registered_date',
                                        read_only=True,
                                        help_text='Date time of registration.')
    embargo_end_date = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='When the embargo on this registration will be lifted.'))

    withdrawal_justification = ser.CharField(source='retraction.justification',
                                             read_only=True)
    template_from = HideIfWithdrawal(
        ser.CharField(
            required=False,
            allow_blank=False,
            allow_null=False,
            help_text=
            'Specify a node id for a node you would like to use as a template for the '
            'new node. Templating is like forking, except that you do not copy the '
            'files, only the project structure. Some information is changed on the top '
            'level project by submitting the appropriate fields in the request body, '
            'and some information will not change. By default, the description will '
            'be cleared and the project will be made private.'))
    registration_supplement = ser.SerializerMethodField()
    registered_meta = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text=
            'A dictionary with supplemental registration questions and responses.'
        ))

    registered_by = HideIfWithdrawal(
        RelationshipField(
            related_view='users:user-detail',
            related_view_kwargs={'user_id': '<registered_user_id>'}))

    registered_from = HideIfWithdrawal(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<registered_from_id>'}))

    children = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-children',
            related_view_kwargs={'node_id': '<pk>'},
            related_meta={'count': 'get_node_count'},
        ))

    comments = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-comments',
                          related_view_kwargs={'node_id': '<pk>'},
                          related_meta={'unread':
                                        'get_unread_comments_count'}))

    contributors = RelationshipField(
        related_view='registrations:registration-contributors',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_contrib_count'})

    files = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-providers',
                          related_view_kwargs={'node_id': '<pk>'}))

    wikis = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-wikis',
            related_view_kwargs={'node_id': '<pk>'},
        ))

    forked_from = HideIfWithdrawal(
        RelationshipField(
            related_view=lambda n: 'registrations:registration-detail'
            if getattr(n, 'is_registration', False) else 'nodes:node-detail',
            related_view_kwargs={'node_id': '<forked_from_id>'}))

    license = HideIfWithdrawal(
        RelationshipField(
            related_view='licenses:license-detail',
            related_view_kwargs={
                'license_id': '<node_license.node_license._id>'
            },
        ))

    forks = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-forks',
                          related_view_kwargs={'node_id': '<pk>'}))

    node_links = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-pointers',
                          related_view_kwargs={'node_id': '<pk>'},
                          related_meta={'count': 'get_pointers_count'}))

    parent = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<parent_node._id>'},
                          filter_key='parent_node'))

    logs = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-logs',
            related_view_kwargs={'node_id': '<pk>'},
        ))

    root = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<root._id>'}))

    affiliated_institutions = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-institutions',
            related_view_kwargs={'node_id': '<pk>'}))
    registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-registrations',
                          related_view_kwargs={'node_id': '<pk>'}))
    identifiers = HideIfWithdrawal(
        RelationshipField(related_view='registrations:identifier-list',
                          related_view_kwargs={'node_id': '<pk>'}))

    # TODO: Finish me

    # TODO: Override create?

    links = LinksField({
        'self': 'get_registration_url',
        'html': 'get_absolute_html_url'
    })

    def get_registration_url(self, obj):
        return absolute_reverse('registrations:registration-detail',
                                kwargs={'node_id': obj._id})

    def get_absolute_url(self, obj):
        return self.get_registration_url(obj)

    def get_registered_meta(self, obj):
        if obj.registered_meta:
            meta_values = obj.registered_meta.values()[0]
            try:
                return json.loads(meta_values)
            except TypeError:
                return meta_values
            except ValueError:
                return meta_values
        return None

    def get_embargo_end_date(self, obj):
        if obj.embargo_end_date:
            return obj.embargo_end_date
        return None

    def get_registration_supplement(self, obj):
        if obj.registered_schema:
            schema = obj.registered_schema[0]
            if schema is None:
                return None
            return schema.name
        return None

    def get_current_user_permissions(self, obj):
        return NodeSerializer.get_current_user_permissions(self, obj)

    def update(self, *args, **kwargs):
        raise exceptions.APIException('Registrations cannot be modified.')

    class Meta:
        type_ = 'registrations'
Esempio n. 7
0
class RegistrationSerializer(NodeSerializer):

    pending_embargo_approval = HideIfRetraction(
        ser.BooleanField(
            read_only=True,
            source='is_pending_embargo',
            help_text=
            'The associated Embargo is awaiting approval by project admins.'))
    pending_registration_approval = HideIfRetraction(
        ser.BooleanField(
            source='is_pending_registration',
            read_only=True,
            help_text=
            'The associated RegistrationApproval is awaiting approval by project admins.'
        ))
    pending_withdrawal = HideIfRetraction(
        ser.BooleanField(
            source='is_pending_retraction',
            read_only=True,
            help_text=
            'The registration is awaiting withdrawal approval by project admins.'
        ))
    withdrawn = ser.BooleanField(
        source='is_retracted',
        read_only=True,
        help_text='The registration has been withdrawn.')

    date_registered = ser.DateTimeField(source='registered_date',
                                        read_only=True,
                                        help_text='Date time of registration.')
    embargo_end_date = HideIfRetraction(
        ser.SerializerMethodField(
            help_text='When the embargo on this registration will be lifted.'))

    withdrawal_justification = ser.CharField(source='retraction.justification',
                                             read_only=True)
    registration_supplement = ser.SerializerMethodField()
    registered_meta = HideIfRetraction(
        ser.SerializerMethodField(
            help_text=
            'A dictionary with supplemental registration questions and responses.'
        ))

    registered_by = HideIfRetraction(
        RelationshipField(
            related_view='users:user-detail',
            related_view_kwargs={'user_id': '<registered_user_id>'}))

    registered_from = HideIfRetraction(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<registered_from_id>'}))

    children = HideIfRetraction(
        RelationshipField(
            related_view='registrations:registration-children',
            related_view_kwargs={'node_id': '<pk>'},
            related_meta={'count': 'get_node_count'},
        ))

    comments = HideIfRetraction(
        RelationshipField(related_view='registrations:registration-comments',
                          related_view_kwargs={'node_id': '<pk>'},
                          related_meta={'unread':
                                        'get_unread_comments_count'}))

    contributors = RelationshipField(
        related_view='registrations:registration-contributors',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_contrib_count'})

    files = HideIfRetraction(
        RelationshipField(related_view='registrations:registration-providers',
                          related_view_kwargs={'node_id': '<pk>'}))

    forked_from = HideIfRetraction(
        RelationshipField(related_view='nodes:node-detail',
                          related_view_kwargs={'node_id': '<forked_from_id>'}))

    node_links = HideIfRetraction(
        RelationshipField(related_view='registrations:registration-pointers',
                          related_view_kwargs={'node_id': '<pk>'},
                          related_meta={'count': 'get_pointers_count'}))

    parent = HideIfRetraction(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<parent_node._id>'},
                          filter_key='parent_node'))

    logs = HideIfRetraction(
        RelationshipField(
            related_view='registrations:registration-logs',
            related_view_kwargs={'node_id': '<pk>'},
        ))

    root = HideIfRetraction(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<root._id>'}))

    primary_institution = RelationshipField(
        related_view='registrations:registration-institution-detail',
        related_view_kwargs={'node_id': '<pk>'})
    registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-registrations',
                          related_view_kwargs={'node_id': '<pk>'}))

    # TODO: Finish me

    # TODO: Override create?

    links = LinksField({
        'self': 'get_registration_url',
        'html': 'get_absolute_html_url'
    })

    def get_registration_url(self, obj):
        return absolute_reverse('registrations:registration-detail',
                                kwargs={'node_id': obj._id})

    def get_absolute_url(self, obj):
        return self.get_registration_url(obj)

    def get_registered_meta(self, obj):
        if obj.registered_meta:
            meta_values = obj.registered_meta.values()[0]
            try:
                return json.loads(meta_values)
            except TypeError:
                return meta_values
            except ValueError:
                return meta_values
        return None

    def get_embargo_end_date(self, obj):
        if obj.embargo_end_date:
            return obj.embargo_end_date
        return None

    def get_registration_supplement(self, obj):
        if obj.registered_schema:
            schema = obj.registered_schema[0]
            if schema is None:
                return None
            return schema.name
        return None

    def update(self, *args, **kwargs):
        raise exceptions.APIException('Registrations cannot be modified.')

    class Meta:
        type_ = 'registrations'