diff --git a/kepi/trilby_api/migrations/0021_auto_20200330_1857.py b/kepi/trilby_api/migrations/0021_auto_20200330_1857.py new file mode 100644 index 0000000..e1d6a9c --- /dev/null +++ b/kepi/trilby_api/migrations/0021_auto_20200330_1857.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.4 on 2020-03-30 18:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('trilby_api', '0020_auto_20200328_1640'), + ] + + operations = [ + migrations.CreateModel( + name='Follow', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('requested', models.BooleanField(default=True)), + ('show_reblogs', models.BooleanField(default=True)), + ('follower', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='following', to='trilby_api.Person')), + ('following', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='followers', to='trilby_api.Person')), + ], + ), + migrations.AddConstraint( + model_name='follow', + constraint=models.UniqueConstraint(fields=('follower', 'following'), name='follow_only_once'), + ), + ] diff --git a/kepi/trilby_api/migrations/0022_person_default_visibility.py b/kepi/trilby_api/migrations/0022_person_default_visibility.py new file mode 100644 index 0000000..cf01af9 --- /dev/null +++ b/kepi/trilby_api/migrations/0022_person_default_visibility.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2020-03-30 19:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trilby_api', '0021_auto_20200330_1857'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='default_visibility', + field=models.CharField(default='public', max_length=255), + ), + ] diff --git a/kepi/trilby_api/migrations/0023_person_default_sensitive.py b/kepi/trilby_api/migrations/0023_person_default_sensitive.py new file mode 100644 index 0000000..b8c22e1 --- /dev/null +++ b/kepi/trilby_api/migrations/0023_person_default_sensitive.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2020-03-30 19:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trilby_api', '0022_person_default_visibility'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='default_sensitive', + field=models.BooleanField(default=False), + ), + ] diff --git a/kepi/trilby_api/models.py b/kepi/trilby_api/models.py index 921e922..e7f6351 100644 --- a/kepi/trilby_api/models.py +++ b/kepi/trilby_api/models.py @@ -123,6 +123,15 @@ class Person(models.Model): "this is where it went." ) + default_visibility = models.CharField( + max_length = 255, + default = 'public', + ) + + default_sensitive = models.BooleanField( + default = False, + ) + @property def uri(self): if self.remote_url is not None: @@ -453,3 +462,47 @@ class Like(models.Model): def __str__(self): return '[%s likes %s]' % (liker, liked) + +##################################### + +class Follow(models.Model): + + follower = models.ForeignKey( + 'Person', + on_delete = models.DO_NOTHING, + related_name = 'following', + ) + + following = models.ForeignKey( + 'Person', + on_delete = models.DO_NOTHING, + related_name = 'followers', + ) + + requested = models.BooleanField( + default=True, + ) + + show_reblogs = models.BooleanField( + default=True, + ) + + class Meta: + constraints = [ + UniqueConstraint( + fields = ['follower', 'following'], + name = 'follow_only_once', + ), + ] + + def __str__(self): + if self.requested: + return '[%s requests to follow %s]' % ( + follower, + following, + ) + else: + return '[%s follows %s]' % ( + follower, + following, + ) diff --git a/kepi/trilby_api/receivers.py b/kepi/trilby_api/receivers.py index d7065e3..2d2c997 100644 --- a/kepi/trilby_api/receivers.py +++ b/kepi/trilby_api/receivers.py @@ -15,7 +15,7 @@ def on_follow(sender, **kwargs): notification = kepi_models.Notification( notification_type = kepi_models.Notification.FOLLOW, - for_account = sender.followed, + for_account = sender.following, about_account = sender.follower, ) diff --git a/kepi/trilby_api/urls.py b/kepi/trilby_api/urls.py index 0ed90b1..07b639d 100644 --- a/kepi/trilby_api/urls.py +++ b/kepi/trilby_api/urls.py @@ -14,11 +14,13 @@ urlpatterns = [ path('api/v1/accounts//statuses', Statuses.as_view()), path('api/v1/accounts//following', Following.as_view()), path('api/v1/accounts//followers', Followers.as_view()), + path('api/v1/accounts//follow', Follow.as_view()), path('api/v1/statuses', Statuses.as_view()), path('api/v1/statuses/', Statuses.as_view()), path('api/v1/statuses//context', StatusContext.as_view()), path('api/v1/statuses//favourite', Favourite.as_view()), + path('api/v1/notifications', Notifications.as_view()), path('api/v1/filters', Filters.as_view()), path('api/v1/custom_emojis', Emojis.as_view()), diff --git a/kepi/trilby_api/views.py b/kepi/trilby_api/views.py index 434312b..70cd5bb 100644 --- a/kepi/trilby_api/views.py +++ b/kepi/trilby_api/views.py @@ -9,7 +9,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.utils.datastructures import MultiValueDictKeyError from django.core.exceptions import SuspiciousOperation from django.conf import settings -from .models import TrilbyUser, Notification, Status +import kepi.trilby_api.models as trilby_models from .serializers import * import kepi.trilby_api.signals as kepi_signals from rest_framework import generics, response @@ -58,13 +58,17 @@ def error_response(status, reason): class DoSomethingWithStatus(generics.GenericAPIView): serializer_class = StatusSerializer - queryset = Status.objects.all() + queryset = trilby_models.Status.objects.all() def _do_something_with(self, the_status, request): raise NotImplementedError() def post(self, request, *args, **kwargs): + if request.user is None: + logger.debug(' -- user not logged in') + return error_response(401, 'Not logged in') + try: the_status = get_object_or_404( self.get_queryset(), @@ -73,10 +77,6 @@ class DoSomethingWithStatus(generics.GenericAPIView): except ValueError: return error_response(404, 'Non-decimal ID') - if request.user is None: - logger.debug(' -- user not logged in') - return error_response(401, 'Not logged in') - self._do_something_with(the_status, request) serializer = StatusSerializer( @@ -97,7 +97,7 @@ class Favourite(DoSomethingWithStatus): def _do_something_with(self, the_status, request): try: - like = Like( + like = trilby_models.Like( liker = request.user.person, liked = the_status, ) @@ -113,6 +113,61 @@ class Favourite(DoSomethingWithStatus): ########################### +class DoSomethingWithPerson(generics.GenericAPIView): + + serializer_class = UserSerializer + queryset = trilby_models.Person.objects.all() + + def _do_something_with(self, the_person, request): + raise NotImplementedError() + + def post(self, request, *args, **kwargs): + + if request.user is None: + logger.debug(' -- user not logged in') + return error_response(401, 'Not logged in') + + the_person = get_object_or_404( + self.get_queryset(), + id = kwargs['name'], + ) + + self._do_something_with(the_person, request) + + serializer = UserSerializer( + the_person, + context = { + 'request': request, + }, + ) + + return JsonResponse( + serializer.data, + status = 200, + reason = 'Done', + ) + +class Follow(DoSomethingWithPerson): + + def _do_something_with(self, the_person, request): + + try: + follow = trilby_models.Follow( + follower = request.user.person, + following = the_person, + ) + + follow.save() + + logger.info(' -- follow: %s', follow) + + kepi_signals.followed.send(sender=follow) + + except IntegrityError: + logger.info(' -- not creating a follow; it already exists') + +########################### + def fix_oauth2_redirects(): """ Called from kepi.kepi.urls to fix a silly oversight @@ -170,7 +225,7 @@ class Verify_Credentials(generics.GenericAPIView): class User(generics.GenericAPIView): - queryset = Person.objects.all() + queryset = trilby_models.Person.objects.all() def get(self, request, *args, **kwargs): whoever = get_object_or_404( @@ -186,7 +241,7 @@ class Statuses(generics.ListCreateAPIView, generics.DestroyAPIView, ): - queryset = Status.objects.filter(remote_url=None) + queryset = trilby_models.Status.objects.filter(remote_url=None) serializer_class = StatusSerializer def get(self, request, *args, **kwargs): @@ -238,7 +293,7 @@ class Statuses(generics.ListCreateAPIView, content = self._string_to_html(data.get('status')) - status = Status( + status = trilby_models.Status( account = request.user.person, content = content, sensitive = data.get('sensitive', False), @@ -269,7 +324,7 @@ class Statuses(generics.ListCreateAPIView, class StatusContext(generics.ListCreateAPIView): - queryset = Status.objects.all() + queryset = trilby_models.Status.objects.all() def get(self, request, *args, **kwargs): @@ -310,7 +365,7 @@ class PublicTimeline(AbstractTimeline): result = [] - timeline = Status.objects.all() + timeline = trilby_models.Status.objects.all() for item in timeline: @@ -337,7 +392,7 @@ class HomeTimeline(AbstractTimeline): # TODO stub class AccountsSearch(generics.ListAPIView): - queryset = Person.objects.all() + queryset = trilby_models.Person.objects.all() serializer_class = UserSerializer permission_classes = [ @@ -371,7 +426,7 @@ class UserFeed(View): def get(self, request, username, *args, **kwargs): - user = get_object_or_404(Person, + user = get_object_or_404(trilby_models.Person, id = '@'+username, )