def post(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data, context={"request": request}) serializer.is_valid(raise_exception=True) user = serializer.validated_data["user"] token, created = Token.objects.get_or_create(user=user) data = LimitedProfileSerializer(user.profile, context={"request": self.request}).data data.update({"token": token.key}) return Response(data)
def post(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data, context={"request": request}) serializer.is_valid(raise_exception=True) user = serializer.validated_data["user"] token, created = Token.objects.get_or_create(user=user) data = LimitedProfileSerializer(user.profile, context={ "request": self.request }).data data.update({"token": token.key}) return Response(data)
def followers(self, request): query_set = self.paginate_queryset( request.user.profile.followers.all()) values = [ LimitedProfileSerializer(x, context={ 'request': request }).data for x in query_set ] return self.get_paginated_response(values)
def get_through_author(self, obj): throughs_authors = self.context.get("throughs_authors") if not throughs_authors: return {} through_author = throughs_authors.get(obj.id, obj.author) if through_author != obj.author: return LimitedProfileSerializer( instance=through_author, read_only=True, context={ "request": self.context.get("request") }, ).data return {}
def collect_data(self): # User serializer = UserSerializer(instance=self.user, context=self.context) self.data['user'] = serializer.data # Profile serializer = ProfileSerializer(instance=self.user.profile, context=self.context) self.data['profile'] = serializer.data # Followed profiles self.data['following'] = [] for follow in self.user.profile.following.all(): serializer = LimitedProfileSerializer(instance=follow, context=self.context) self.data['following'].append(serializer.data) # Content self.data['content'] = [] content_qs = Content.objects.filter( author=self.user.profile).order_by('created') for content in content_qs: serializer = ContentSerializer(instance=content, context=self.context) self.data['content'].append(serializer.data)
class ContentSerializer(serializers.ModelSerializer): author = LimitedProfileSerializer(read_only=True) content_type = EnumField(ContentType, ints_as_names=True, read_only=True) user_following_author = SerializerMethodField() user_is_author = SerializerMethodField() user_has_shared = SerializerMethodField() tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field="name") through = SerializerMethodField() visibility = EnumField(Visibility, lenient=True, ints_as_names=True, required=False) class Meta: model = Content fields = ( "author", "content_type", "edited", "uuid", "has_twitter_oembed", "humanized_timestamp", "id", "is_nsfw", "local", "order", "parent", "pinned", "remote_created", "rendered", "reply_count", "service_label", "share_of", "shares_count", "tags", "text", "through", "timestamp", "url", "user_following_author", "user_is_author", "user_has_shared", "visibility", ) read_only_fields = ( "author", "content_type" "edited", "uuid", "has_twitter_oembed", "humanized_timestamp", "id", "is_nsfw", "local", "remote_created", "rendered", "reply_count", "share_of", "shares_count", "tags", "through", "timestamp", "url", "user_following_author", "user_is_author", "user_has_shared", ) def get_through(self, obj): """Through is generally required only for serializing content for streams.""" throughs = self.context.get("throughs") if not throughs: return obj.id return throughs.get(obj.id, obj.id) def get_user_following_author(self, obj): request = self.context.get("request") if not request: return False return bool(request.user.is_authenticated and obj.author_id in request.user.profile.following_ids) def get_user_is_author(self, obj): request = self.context.get("request") if not request: return False return bool(request.user.is_authenticated and obj.author == request.user.profile) def get_user_has_shared(self, obj): request = self.context.get("request") if not request: return False return Content.has_shared(obj.id, request.user.profile.id) if hasattr(request.user, "profile") else False def validate(self, data): """ Validate visibility is not required for replies. If given, make sure it is the same as parent. If not given, use parent visibility. """ parent = data.get("parent") if parent: if data.get("visibility") and parent.visibility != data.get("visibility"): raise serializers.ValidationError("Visibility was given but it doesn't match parent.") data["visibility"] = parent.visibility else: if not self.instance and not data.get("visibility"): raise serializers.ValidationError("Visibility is required") return data def validate_parent(self, value): # Validate parent cannot be changed if self.instance and value != self.instance.parent: raise serializers.ValidationError("Parent cannot be changed for an existing Content instance.") # Validate user can see parent if not self.instance and value: request = self.context.get("request") if not value.visible_for_user(request.user): raise serializers.ValidationError("Parent not found") return value def validate_visibility(self, value): """ Don't allow creating Limited visibility as of now. """ if value == Visibility.LIMITED and not self.instance: raise serializers.ValidationError("Limited content creation not yet supported via the API.") return value
class ContentSerializer(serializers.ModelSerializer): author = LimitedProfileSerializer(read_only=True) content_type = EnumField(ContentType, ints_as_names=True, read_only=True) include_following = BooleanField(default=False) recipients = RecipientsField() user_is_author = SerializerMethodField() user_has_shared = SerializerMethodField() tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field="name") through = SerializerMethodField() through_author = SerializerMethodField() visibility = EnumField(Visibility, lenient=True, ints_as_names=True, required=False) class Meta: model = Content fields = ( "author", "content_type", "edited", "federate", "uuid", "has_twitter_oembed", "humanized_timestamp", "id", "is_nsfw", "include_following", "local", "order", "parent", "pinned", "recipients", "remote_created", "rendered", "reply_count", "root_parent", "service_label", "share_of", "shares_count", "show_preview", "tags", "text", "through", "through_author", "timestamp", "url", "user_is_author", "user_has_shared", "visibility", ) read_only_fields = ( "author", "content_type" "edited", "uuid", "has_twitter_oembed", "humanized_timestamp", "id", "is_nsfw", "local", "remote_created", "rendered", "reply_count", "root_parent", "share_of", "shares_count", "tags", "through", "through_author", "timestamp", "url", "user_is_author", "user_has_shared", ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.cache_through_authors() def cache_through_authors(self): """ If we have 'throughs', cache author information here for all of them. """ request = self.context.get("request") if not self.context.get("throughs") or not request: self.context["throughs_authors"] = {} return throughs_ids = self.context["throughs"] ids = {value for _key, value in throughs_ids.items()} through_to_id = {value: key for key, value in throughs_ids.items()} throughs = Content.objects.visible_for_user( request.user).select_related("author").filter(id__in=list(ids)) self.context["throughs_authors"] = { through_to_id.get(c.id, c.id): c.author for c in throughs } def get_through(self, obj): """Through is generally required only for serializing content for streams.""" throughs = self.context.get("throughs") if not throughs: return obj.id return throughs.get(obj.id, obj.id) def get_through_author(self, obj): throughs_authors = self.context.get("throughs_authors") if not throughs_authors: return {} through_author = throughs_authors.get(obj.id, obj.author) if through_author != obj.author: return LimitedProfileSerializer( instance=through_author, read_only=True, context={ "request": self.context.get("request") }, ).data return {} def get_user_is_author(self, obj): request = self.context.get("request") if not request: return False return bool(request.user.is_authenticated and obj.author == request.user.profile) def get_user_has_shared(self, obj): request = self.context.get("request") if not request: return False return Content.has_shared(obj.id, request.user.profile.id) if hasattr( request.user, "profile") else False def validate_parent(self, value): # Validate parent cannot be changed if self.instance and value != self.instance.parent: raise serializers.ValidationError( "Parent cannot be changed for an existing Content instance.") # Validate user can see parent if not self.instance and value: request = self.context.get("request") if not value.visible_for_user(request.user): raise serializers.ValidationError("Parent not found") return value def to_representation(self, instance: Content) -> Dict[str, Any]: result = dict(super().to_representation(instance)) if not self.get_user_is_author(instance): result["recipients"] = "" return result def save(self, **kwargs: Dict): """ Set possible recipients after save. """ updating = self.instance is not None raw_recipients = self.validated_data.pop("recipients", "") parent = self.validated_data.get("parent") user = self.context.get("request").user # Get previous recipients, if old instance previous_recipients = [] if updating: if not self.instance.author_id: self.instance.author = user.profile if self.instance.limited_visibilities.count(): previous_recipients = self.instance.limited_visibilities.values_list( "id", flat=True) if parent is not None: self.instance.parent = parent # HAXX to ensure visibility is empty when going in to save self.instance.visibility = None content = super().save(**kwargs) if content.visibility != Visibility.LIMITED or content.content_type == ContentType.SHARE: return content if content.content_type == ContentType.CONTENT: # Collect new recipients recipients = Profile.objects.filter( Q(handle__in=raw_recipients) | Q(fid__in=raw_recipients)).visible_for_user(user) # Add following, if included if self.validated_data.get("include_following"): recipients = recipients | user.profile.following.all() recipients = recipients.distinct() elif content.content_type == ContentType.REPLY: recipients = content.root_parent.limited_visibilities.all() else: return content # If old instance, first remove the now not present to trigger federation retraction if previous_recipients: to_remove = set(previous_recipients).difference( set(recipients.values_list("id", flat=True))) for remove_id in to_remove: profile = Profile.objects.get(id=remove_id) content.limited_visibilities.remove(profile) # Clear, then set, since federation will be triggered by m2m changed signal content.limited_visibilities.clear() content.limited_visibilities.set(recipients) return content def validate(self, data): """ Validate visibility is not required for replies. If given, make sure it is the same as parent. If not given, use parent visibility. """ parent = data.get("parent") if parent: if data.get("visibility" ) and parent.visibility != data.get("visibility"): raise serializers.ValidationError( "Visibility was given but it doesn't match parent.") data["visibility"] = parent.visibility else: if not self.instance and not data.get("visibility"): raise serializers.ValidationError("Visibility is required") return data def validate_text(self, value): """Sanitize text if user is untrusted.""" user = self.context.get("request").user if user.trusted_editor: return value return safe_text_for_markdown(value) def validate_recipients(self, value: Set[str]) -> Set[str]: if self.initial_data.get( "visibility", Visibility.PUBLIC.value) != Visibility.LIMITED.value: return value if not value and not self.initial_data.get("include_following"): raise serializers.ValidationError( "Recipients cannot be empty if not including followed users.") user = self.context.get("request").user validation_errors = [] for recipient in value: if not validate_handle(recipient) and not re.match( r"https?://", recipient): validation_errors.append(recipient) if len(validation_errors) > 0: msg = _( "This recipient couldn't be found (please check the format).", "These recipients couldn't be found (please check the format).", len(validation_errors)) raise serializers.ValidationError({ "code": "recipients_not_found_error", "message": msg, "payload": validation_errors, }) recipient_profiles = Profile.objects.filter( Q(handle__in=value) | Q(fid__in=value)).visible_for_user(user) # TODO we should probably try to lookup missing ones over the network first before failing if recipient_profiles.distinct().count() != len(set(value)): raise serializers.ValidationError( "Not all recipients could be found.") return value
class ContentSerializer(ModelSerializer): author = LimitedProfileSerializer(read_only=True) content_type = EnumField(ContentType, ints_as_names=True, read_only=True) user_following_author = SerializerMethodField() user_is_author = SerializerMethodField() user_has_shared = SerializerMethodField() tags = SlugRelatedField(many=True, read_only=True, slug_field="name") through = SerializerMethodField() visibility = EnumField(Visibility, lenient=True, ints_as_names=True) class Meta: model = Content fields = ( "author", "content_type", "edited", "guid", "humanized_timestamp", "id", "is_nsfw", "local", "order", "parent", "pinned", "remote_created", "rendered", "reply_count", "service_label", "share_of", "shares_count", "tags", "text", "through", "timestamp", "url", "user_following_author", "user_is_author", "user_has_shared", "visibility", ) read_only_fields = ( "author", "content_type" "edited", "guid", "humanized_timestamp", "id", "is_nsfw", "local", "parent", "remote_created", "rendered", "reply_count", "share_of", "shares_count", "tags", "through", "timestamp", "url", "user_following_author", "user_is_author", "user_has_shared", ) def get_through(self, obj): """Through is generally required only for serializing content for streams.""" throughs = self.context.get("throughs") if not throughs: return obj.id return throughs.get(obj.id, obj.id) def get_user_following_author(self, obj): request = self.context.get("request") if not request: return False return bool(request.user.is_authenticated and obj.author_id in request.user.profile.following_ids) def get_user_is_author(self, obj): request = self.context.get("request") if not request: return False return bool(request.user.is_authenticated and obj.author == request.user.profile) def get_user_has_shared(self, obj): request = self.context.get("request") if not request: return False return Content.has_shared(obj.id, request.user.profile.id) if hasattr( request.user, "profile") else False