kopia lustrzana https://gitlab.com/marnanel/chapeau
Porównaj commity
8 Commity
aee804b6ae
...
1c3a4733d8
Autor | SHA1 | Data |
---|---|---|
Marnanel Thurman | 1c3a4733d8 | |
Marnanel Thurman | 44b4aaaad7 | |
Marnanel Thurman | c8a5b5308b | |
Marnanel Thurman | 94309cc77d | |
Marnanel Thurman | b00f844d84 | |
Marnanel Thurman | 8a0deb6c93 | |
Marnanel Thurman | 4d63fd669d | |
Thomas Thurman | 07875e03ab |
|
@ -1,5 +1,3 @@
|
||||||
from django.dispatch import Signal
|
from django.dispatch import Signal
|
||||||
|
|
||||||
received = Signal(
|
received = Signal()
|
||||||
providing_args=[
|
|
||||||
])
|
|
||||||
|
|
|
@ -19,4 +19,5 @@ urlpatterns = [
|
||||||
path('users/<str:username>/following', bowler_pub_views.FollowingView.as_view()),
|
path('users/<str:username>/following', bowler_pub_views.FollowingView.as_view()),
|
||||||
path('users/<str:username>/featured', bowler_pub_views.FeaturedView.as_view()),
|
path('users/<str:username>/featured', bowler_pub_views.FeaturedView.as_view()),
|
||||||
path('sharedInbox', bowler_pub_views.InboxView.as_view()),
|
path('sharedInbox', bowler_pub_views.InboxView.as_view()),
|
||||||
|
path('inbox', bowler_pub_views.InboxView.as_view()), # config error, marnanel.org specific
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import django
|
||||||
|
|
||||||
|
logger = logging.getLogger('kepi')
|
||||||
|
|
||||||
|
class KepiTestCase(django.test.TestCase):
|
||||||
|
"""
|
||||||
|
A test case.
|
||||||
|
|
||||||
|
It turns on logging to stdout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self._logging_stream_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
logger.addHandler(self._logging_stream_handler)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
logger.removeHandler(self._logging_stream_handler)
|
|
@ -0,0 +1,5 @@
|
||||||
|
import django.apps
|
||||||
|
|
||||||
|
class SombreroApiConfig(django.apps.AppConfig):
|
||||||
|
name = 'kepi.sombrero_sendpub'
|
||||||
|
default_auto_field = 'django.db.models.AutoField'
|
|
@ -8,12 +8,12 @@ import logging
|
||||||
logger = logging.getLogger(name="kepi")
|
logger = logging.getLogger(name="kepi")
|
||||||
|
|
||||||
from unittest import skip
|
from unittest import skip
|
||||||
from django.test import TestCase
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from kepi.sombrero_sendpub.fetch import fetch
|
from kepi.sombrero_sendpub.fetch import fetch
|
||||||
from kepi.trilby_api.models import RemotePerson, Person, Status
|
from kepi.trilby_api.models import RemotePerson, Person, Status
|
||||||
from kepi.trilby_api.tests import create_local_person
|
from kepi.trilby_api.tests import create_local_person
|
||||||
from kepi.sombrero_sendpub.collections import Collection
|
from kepi.sombrero_sendpub.collections import Collection
|
||||||
|
from kepi.kepi.testing import KepiTestCase
|
||||||
from . import suppress_thread_exceptions
|
from . import suppress_thread_exceptions
|
||||||
import httpretty
|
import httpretty
|
||||||
import requests
|
import requests
|
||||||
|
@ -166,7 +166,7 @@ EXAMPLE_COMPLEX_COLLECTION_PAGE_2 = """{
|
||||||
EXAMPLE_COMPLEX_COLLECTION_URL,
|
EXAMPLE_COMPLEX_COLLECTION_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
class TestFetchRemoteUser(TestCase):
|
class TestFetchRemoteUser(KepiTestCase):
|
||||||
|
|
||||||
@httpretty.activate
|
@httpretty.activate
|
||||||
def test_fetch(self):
|
def test_fetch(self):
|
||||||
|
@ -527,9 +527,10 @@ class TestFetchRemoteUser(TestCase):
|
||||||
len(EXAMPLE_COMPLEX_COLLECTION_MEMBERS),
|
len(EXAMPLE_COMPLEX_COLLECTION_MEMBERS),
|
||||||
msg="Collection has a length")
|
msg="Collection has a length")
|
||||||
|
|
||||||
class TestFetchLocalUser(TestCase):
|
class TestFetchLocalUser(KepiTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
self._alice = create_local_person(
|
self._alice = create_local_person(
|
||||||
name = 'alice',
|
name = 'alice',
|
||||||
)
|
)
|
||||||
|
@ -588,5 +589,5 @@ class TestFetchLocalUser(TestCase):
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
class TestFetchStatus(TestCase):
|
class TestFetchStatus(KepiTestCase):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,4 +2,11 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{bio}}
|
{{bio}}
|
||||||
{% endblock %}
|
|
||||||
|
{% for s in statuses %}
|
||||||
|
{{s}}
|
||||||
|
<p xml:lang="{{ s.object__find.language }}">
|
||||||
|
{{ s.object__find.content | escape }}
|
||||||
|
</p>
|
||||||
|
{% endfor %}
|
||||||
|
<{% endblock %}
|
||||||
|
|
|
@ -13,6 +13,7 @@ from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
import kepi.trilby_api.models as trilby_models
|
import kepi.trilby_api.models as trilby_models
|
||||||
|
import kepi.tophat_ui.models as tophat_models
|
||||||
|
|
||||||
class RootPage(View):
|
class RootPage(View):
|
||||||
|
|
||||||
|
@ -46,6 +47,10 @@ class UserPage(View):
|
||||||
local_user__username = username,
|
local_user__username = username,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
statuses = trilby_models.Status.objects.filter(
|
||||||
|
account = user,
|
||||||
|
)
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
request=request,
|
request=request,
|
||||||
template_name='user-page.html',
|
template_name='user-page.html',
|
||||||
|
@ -53,6 +58,7 @@ class UserPage(View):
|
||||||
'title': user.username,
|
'title': user.username,
|
||||||
'subtitle': user.display_name,
|
'subtitle': user.display_name,
|
||||||
'bio': user.note,
|
'bio': user.note,
|
||||||
|
'statuses': statuses,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.apps import AppConfig
|
import django.apps
|
||||||
|
|
||||||
|
class TrilbyApiConfig(django.apps.AppConfig):
|
||||||
class TrilbyApiConfig(AppConfig):
|
|
||||||
name = 'kepi.trilby_api'
|
name = 'kepi.trilby_api'
|
||||||
|
default_auto_field = 'django.db.models.AutoField'
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
# Copyright (c) 2018-2021 Marnanel Thurman.
|
# Copyright (c) 2018-2021 Marnanel Thurman.
|
||||||
# Licensed under the GNU Public License v2.
|
# Licensed under the GNU Public License v2.
|
||||||
|
|
||||||
from django.test import TestCase, Client
|
from django.test import Client
|
||||||
|
from kepi.kepi.testing import KepiTestCase
|
||||||
from rest_framework.test import force_authenticate, APIClient
|
from rest_framework.test import force_authenticate, APIClient
|
||||||
from kepi.trilby_api.models import *
|
from kepi.trilby_api.models import *
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -65,7 +66,7 @@ STATUS_EXPECTED = {
|
||||||
'pinned': False,
|
'pinned': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
class TrilbyTestCase(TestCase):
|
class TrilbyTestCase(KepiTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from kepi.trilby_api.utils import *
|
from kepi.trilby_api.utils import *
|
||||||
|
from kepi.kepi.testing import KepiTestCase
|
||||||
|
|
||||||
class Tests(TestCase):
|
class Tests(KepiTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
settings.KEPI['LOCAL_OBJECT_HOSTNAME'] = 'testserver'
|
settings.KEPI['LOCAL_OBJECT_HOSTNAME'] = 'testserver'
|
||||||
|
|
||||||
def test_is_local_user_url(self):
|
def test_is_local_user_url(self):
|
||||||
|
|
|
@ -5,48 +5,48 @@
|
||||||
# Licensed under the GNU Public License v2.
|
# Licensed under the GNU Public License v2.
|
||||||
|
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from .views import *
|
import kepi.trilby_api.views as views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
path('api/v1/instance', Instance.as_view()),
|
path('api/v1/instance', views.Instance.as_view()),
|
||||||
path('api/v1/instance/', Instance.as_view()), # keep tootstream happy
|
path('api/v1/instance/', views.Instance.as_view()), # keep tootstream happy
|
||||||
path('api/v1/apps', Apps.as_view()),
|
path('api/v1/apps', views.Apps.as_view()),
|
||||||
|
|
||||||
path('api/v1/accounts/verify_credentials', VerifyCredentials.as_view()),
|
path('api/v1/accounts/verify_credentials', views.VerifyCredentials.as_view()),
|
||||||
path('api/v1/accounts/update_credentials',
|
path('api/v1/accounts/update_credentials',
|
||||||
UpdateCredentials.as_view()),
|
views.UpdateCredentials.as_view()),
|
||||||
|
|
||||||
path('api/v1/accounts/search', AccountsSearch.as_view()),
|
path('api/v1/accounts/search', views.AccountsSearch.as_view()),
|
||||||
|
|
||||||
path('api/v1/accounts/<user>', User.as_view()),
|
path('api/v1/accounts/<user>', views.User.as_view()),
|
||||||
path('api/v1/accounts/<user>/statuses', Statuses.as_view()),
|
path('api/v1/accounts/<user>/statuses', views.Statuses.as_view()),
|
||||||
path('api/v1/accounts/<user>/following', Following.as_view()),
|
path('api/v1/accounts/<user>/following', views.Following.as_view()),
|
||||||
path('api/v1/accounts/<user>/followers', Followers.as_view()),
|
path('api/v1/accounts/<user>/followers', views.Followers.as_view()),
|
||||||
path('api/v1/accounts/<user>/follow', Follow.as_view()),
|
path('api/v1/accounts/<user>/follow', views.FollowUser.as_view()),
|
||||||
path('api/v1/accounts/<user>/unfollow', Unfollow.as_view()),
|
path('api/v1/accounts/<user>/unfollow', views.UnfollowUser.as_view()),
|
||||||
|
|
||||||
path('api/v1/statuses', Statuses.as_view()),
|
path('api/v1/statuses', views.Statuses.as_view()),
|
||||||
path('api/v1/statuses/<status>', SpecificStatus.as_view()),
|
path('api/v1/statuses/<status>', views.SpecificStatus.as_view()),
|
||||||
path('api/v1/statuses/<status>/context', StatusContext.as_view()),
|
path('api/v1/statuses/<status>/context', views.StatusContext.as_view()),
|
||||||
|
|
||||||
# Favourite, aka like
|
# Favourite, aka like
|
||||||
path('api/v1/statuses/<status>/favourite', Favourite.as_view()),
|
path('api/v1/statuses/<status>/favourite', views.Favourite.as_view()),
|
||||||
path('api/v1/statuses/<status>/unfavourite', Unfavourite.as_view()),
|
path('api/v1/statuses/<status>/unfavourite', views.Unfavourite.as_view()),
|
||||||
path('api/v1/statuses/<status>/favourited_by', StatusFavouritedBy.as_view()),
|
path('api/v1/statuses/<status>/favourited_by', views.StatusFavouritedBy.as_view()),
|
||||||
|
|
||||||
# Reblog, aka boost
|
# Reblog, aka boost
|
||||||
path('api/v1/statuses/<status>/reblog', Reblog.as_view()),
|
path('api/v1/statuses/<status>/reblog', views.Reblog.as_view()),
|
||||||
path('api/v1/statuses/<status>/unreblog', Unreblog.as_view()),
|
path('api/v1/statuses/<status>/unreblog', views.Unreblog.as_view()),
|
||||||
path('api/v1/statuses/<status>/reblogged_by', StatusRebloggedBy.as_view()),
|
path('api/v1/statuses/<status>/reblogged_by', views.StatusRebloggedBy.as_view()),
|
||||||
|
|
||||||
path('api/v1/notifications', Notifications.as_view()),
|
path('api/v1/notifications', views.Notifications.as_view()),
|
||||||
path('api/v1/filters', Filters.as_view()),
|
path('api/v1/filters', views.Filters.as_view()),
|
||||||
path('api/v1/custom_emojis', Emojis.as_view()),
|
path('api/v1/custom_emojis', views.Emojis.as_view()),
|
||||||
path('api/v1/timelines/public', PublicTimeline.as_view()),
|
path('api/v1/timelines/public', views.PublicTimeline.as_view()),
|
||||||
path('api/v1/timelines/home', HomeTimeline.as_view()),
|
path('api/v1/timelines/home', views.HomeTimeline.as_view()),
|
||||||
|
|
||||||
path('api/v1/search', Search.as_view()),
|
path('api/v1/search', views.Search.as_view()),
|
||||||
|
|
||||||
path('users/<username>/feed', UserFeed.as_view()),
|
path('users/<username>/feed', views.UserFeed.as_view()),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,73 +1,38 @@
|
||||||
# trilby_api/views/__init__.py
|
from .oauth import *
|
||||||
#
|
from .other import *
|
||||||
# Part of kepi.
|
from .persons import *
|
||||||
# Copyright (c) 2018-2021 Marnanel Thurman.
|
from .statuses import *
|
||||||
# Licensed under the GNU Public License v2.
|
from .timelines import *
|
||||||
|
|
||||||
from .other import \
|
|
||||||
Instance, \
|
|
||||||
Emojis, \
|
|
||||||
Filters, \
|
|
||||||
Search, \
|
|
||||||
AccountsSearch
|
|
||||||
|
|
||||||
from .statuses import \
|
|
||||||
Favourite, Unfavourite, \
|
|
||||||
Reblog, Unreblog, \
|
|
||||||
SpecificStatus, \
|
|
||||||
Statuses, \
|
|
||||||
StatusContext, \
|
|
||||||
StatusFavouritedBy, \
|
|
||||||
StatusRebloggedBy, \
|
|
||||||
Notifications
|
|
||||||
|
|
||||||
from .persons import \
|
|
||||||
Follow, Unfollow, \
|
|
||||||
UpdateCredentials, \
|
|
||||||
VerifyCredentials,\
|
|
||||||
User, \
|
|
||||||
Followers, Following
|
|
||||||
|
|
||||||
from .oauth import \
|
|
||||||
Apps, \
|
|
||||||
fix_oauth2_redirects
|
|
||||||
|
|
||||||
from .timelines import \
|
|
||||||
PublicTimeline, \
|
|
||||||
HomeTimeline, \
|
|
||||||
UserFeed
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# other
|
'AbstractTimeline',
|
||||||
'Instance',
|
|
||||||
'Emojis',
|
|
||||||
'Filters',
|
|
||||||
'Search',
|
|
||||||
'AccountsSearch',
|
'AccountsSearch',
|
||||||
|
'Apps',
|
||||||
# statuses
|
'DoSomethingWithPerson',
|
||||||
'Favourite', 'Unfavourite',
|
'DoSomethingWithStatus',
|
||||||
'Reblog', 'Unreblog',
|
'Emojis',
|
||||||
|
'Favourite',
|
||||||
|
'Filters',
|
||||||
|
'FollowUser',
|
||||||
|
'Followers',
|
||||||
|
'Followers_or_Following',
|
||||||
|
'Following',
|
||||||
|
'HomeTimeline',
|
||||||
|
'Instance',
|
||||||
|
'Notifications',
|
||||||
|
'PublicTimeline',
|
||||||
|
'Reblog',
|
||||||
|
'Search',
|
||||||
'SpecificStatus',
|
'SpecificStatus',
|
||||||
'Statuses',
|
|
||||||
'StatusContext',
|
'StatusContext',
|
||||||
|
'Statuses',
|
||||||
'StatusFavouritedBy',
|
'StatusFavouritedBy',
|
||||||
'StatusRebloggedBy',
|
'StatusRebloggedBy',
|
||||||
'Notifications',
|
'Unfavourite',
|
||||||
|
'UnfollowUser',
|
||||||
# persons
|
'Unreblog',
|
||||||
'Follow', 'Unfollow',
|
|
||||||
'UpdateCredentials',
|
'UpdateCredentials',
|
||||||
'VerifyCredentials',
|
|
||||||
'User',
|
'User',
|
||||||
'Followers', 'Following',
|
|
||||||
|
|
||||||
# oauth
|
|
||||||
'Apps',
|
|
||||||
'fix_oauth2_redirects',
|
|
||||||
|
|
||||||
# timelines
|
|
||||||
'PublicTimeline',
|
|
||||||
'HomeTimeline',
|
|
||||||
'UserFeed',
|
'UserFeed',
|
||||||
|
'VerifyCredentials',
|
||||||
]
|
]
|
||||||
|
|
|
@ -71,7 +71,7 @@ class DoSomethingWithPerson(generics.GenericAPIView):
|
||||||
reason = 'Done',
|
reason = 'Done',
|
||||||
)
|
)
|
||||||
|
|
||||||
class Follow(DoSomethingWithPerson):
|
class FollowUser(DoSomethingWithPerson):
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
def _do_something_with(self, the_person, request):
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ class Follow(DoSomethingWithPerson):
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
logger.info(' -- not creating a follow; it already exists')
|
logger.info(' -- not creating a follow; it already exists')
|
||||||
|
|
||||||
class Unfollow(DoSomethingWithPerson):
|
class UnfollowUser(DoSomethingWithPerson):
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
def _do_something_with(self, the_person, request):
|
||||||
|
|
||||||
|
@ -142,76 +142,6 @@ class Unfollow(DoSomethingWithPerson):
|
||||||
logger.info(' -- not unfollowing; they weren\'t following '+\
|
logger.info(' -- not unfollowing; they weren\'t following '+\
|
||||||
'in the first place')
|
'in the first place')
|
||||||
|
|
||||||
class UpdateCredentials(generics.GenericAPIView):
|
|
||||||
|
|
||||||
def patch(self, request, *args, **kwargs):
|
|
||||||
|
|
||||||
if request.user is None:
|
|
||||||
logger.debug(' -- user not logged in')
|
|
||||||
return error_response(401, 'Not logged in')
|
|
||||||
|
|
||||||
who = request.user.localperson
|
|
||||||
|
|
||||||
# The Mastodon spec doesn't say what to do
|
|
||||||
# if the user submits field names which don't
|
|
||||||
# exist!
|
|
||||||
|
|
||||||
unknown_fields = []
|
|
||||||
|
|
||||||
# FIXME: the data in "v" needs cleaning.
|
|
||||||
|
|
||||||
logger.info('-- updating user: %s', who)
|
|
||||||
|
|
||||||
for f,v in request.data.items():
|
|
||||||
|
|
||||||
logger.info(' -- setting %s = %s', f, v)
|
|
||||||
|
|
||||||
if f=='discoverable':
|
|
||||||
raise Http404("discoverable is not yet supported")
|
|
||||||
elif f=='bot':
|
|
||||||
who.bot = v
|
|
||||||
elif f=='display_name':
|
|
||||||
who.display_name = v
|
|
||||||
elif f=='note':
|
|
||||||
who.note = v
|
|
||||||
elif f=='avatar':
|
|
||||||
raise Http404("images are not yet supported")
|
|
||||||
elif f=='header':
|
|
||||||
raise Http404("images are not yet supported")
|
|
||||||
elif f=='locked':
|
|
||||||
who.locked = v
|
|
||||||
elif f=='source[privacy]':
|
|
||||||
who.default_visibility = v
|
|
||||||
elif f=='source[sensitive]':
|
|
||||||
who.default_sensitive = v
|
|
||||||
elif f=='source[language]':
|
|
||||||
who.language = v
|
|
||||||
elif f=='fields_attributes':
|
|
||||||
raise Http404("fields are not yet supported")
|
|
||||||
else:
|
|
||||||
logger.info(' -- field does not exist')
|
|
||||||
unknown_fields.append(f)
|
|
||||||
|
|
||||||
if unknown_fields:
|
|
||||||
logger.info(' -- aborting because of unknown fields')
|
|
||||||
raise Http404(f"some fields do not exist: {unknown_fields}")
|
|
||||||
|
|
||||||
who.save()
|
|
||||||
logger.info(' -- done.')
|
|
||||||
|
|
||||||
serializer = UserSerializerWithSource(
|
|
||||||
who,
|
|
||||||
context = {
|
|
||||||
'request': request,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return JsonResponse(
|
|
||||||
serializer.data,
|
|
||||||
status = 200,
|
|
||||||
reason = 'Done',
|
|
||||||
)
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
|
|
||||||
class VerifyCredentials(generics.GenericAPIView):
|
class VerifyCredentials(generics.GenericAPIView):
|
||||||
|
|
|
@ -183,117 +183,6 @@ class Unreblog(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')
|
|
||||||
|
|
||||||
try:
|
|
||||||
the_person = get_object_or_404(
|
|
||||||
self.get_queryset(),
|
|
||||||
id = int(kwargs['user']),
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
return error_response(404, 'Non-decimal ID')
|
|
||||||
|
|
||||||
result = self._do_something_with(the_person, request)
|
|
||||||
|
|
||||||
if result is None:
|
|
||||||
result = the_person
|
|
||||||
|
|
||||||
serializer = UserSerializer(
|
|
||||||
result,
|
|
||||||
context = {
|
|
||||||
'request': request,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return JsonResponse(
|
|
||||||
serializer.data,
|
|
||||||
status = 200,
|
|
||||||
reason = 'Done',
|
|
||||||
)
|
|
||||||
|
|
||||||
class Follow(DoSomethingWithPerson):
|
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
if the_person.auto_follow:
|
|
||||||
offer = None
|
|
||||||
else:
|
|
||||||
number = random.randint(0, 0xffffffff)
|
|
||||||
offer = uri_to_url(settings.KEPI['FOLLOW_REQUEST_LINK'] % {
|
|
||||||
'username': request.user.username,
|
|
||||||
'number': number,
|
|
||||||
})
|
|
||||||
|
|
||||||
follow = trilby_models.Follow(
|
|
||||||
follower = request.user.localperson,
|
|
||||||
following = the_person,
|
|
||||||
offer = offer,
|
|
||||||
)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
follow.save(
|
|
||||||
send_signal = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(' -- follow: %s', follow)
|
|
||||||
logger.debug(' -- offer ID: %s', offer)
|
|
||||||
|
|
||||||
if the_person.auto_follow:
|
|
||||||
follow_back = trilby_models.Follow(
|
|
||||||
follower = the_person,
|
|
||||||
following = request.user.localperson,
|
|
||||||
offer = None,
|
|
||||||
)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
follow_back.save(
|
|
||||||
send_signal = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(' -- follow back: %s', follow_back)
|
|
||||||
|
|
||||||
return the_person
|
|
||||||
|
|
||||||
except IntegrityError:
|
|
||||||
logger.info(' -- not creating a follow; it already exists')
|
|
||||||
|
|
||||||
class Unfollow(DoSomethingWithPerson):
|
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
|
||||||
|
|
||||||
try:
|
|
||||||
follow = trilby_models.Follow.objects.get(
|
|
||||||
follower = request.user.localperson,
|
|
||||||
following = the_person,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(' -- unfollowing: %s', follow)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
follow.delete(
|
|
||||||
send_signal = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return the_person
|
|
||||||
|
|
||||||
except trilby_models.Follow.DoesNotExist:
|
|
||||||
logger.info(' -- not unfollowing; they weren\'t following '+\
|
|
||||||
'in the first place')
|
|
||||||
|
|
||||||
class SpecificStatus(generics.GenericAPIView):
|
class SpecificStatus(generics.GenericAPIView):
|
||||||
|
|
||||||
queryset = trilby_models.Status.objects.filter(remote_url=None)
|
queryset = trilby_models.Status.objects.filter(remote_url=None)
|
||||||
|
|
Ładowanie…
Reference in New Issue