Merge pull request #58 from doubaniux/sites-auth

update cross site auth
This commit is contained in:
doubaniux 2020-10-22 21:47:41 +02:00 committed by GitHub
commit a0562c5e59
56 changed files with 694 additions and 282 deletions

View file

@ -14,7 +14,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
<body>
@ -42,7 +42,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -15,7 +15,7 @@
<script src="{% static 'js/create_update_review.js' %}"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>
@ -98,7 +98,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -17,7 +17,7 @@
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
<body>
@ -78,7 +78,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -14,7 +14,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
</head>
@ -94,7 +94,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -30,7 +30,7 @@
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_modal.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
</head>

View file

@ -15,7 +15,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
@ -131,7 +131,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -22,7 +22,7 @@
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
<body>
@ -111,7 +111,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -16,7 +16,7 @@
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>
@ -118,7 +118,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -13,7 +13,7 @@
<title>{% trans 'NiceDB - 从豆瓣获取数据' %}</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'js/scrape.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>

View file

@ -9,9 +9,9 @@ from django.db.models import Count
from django.utils import timezone
from django.core.paginator import Paginator
from django.core.files.uploadedfile import SimpleUploadedFile
from common.mastodon import mastodon_request_included
from common.mastodon.api import check_visibility, post_toot, TootVisibilityEnum
from common.mastodon.utils import rating_to_emoji
from mastodon import mastodon_request_included
from mastodon.api import check_visibility, post_toot, TootVisibilityEnum
from mastodon.utils import rating_to_emoji
from common.utils import PageLinksGenerator
from common.views import PAGE_LINK_NUMBER
from .models import *

View file

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View file

@ -1,94 +0,0 @@
import requests
import string
import random
import functools
from boofilsic.settings import MASTODON_TIMEOUT, MASTODON_DOMAIN_NAME
# See https://docs.joinmastodon.org/methods/accounts/
# returns user info
# retruns the same info as verify account credentials
# GET
API_GET_ACCOUNT = '/api/v1/accounts/:id'
# returns user info if valid, 401 if invalid
# GET
API_VERIFY_ACCOUNT = '/api/v1/accounts/verify_credentials'
# obtain token
# GET
API_OBTAIN_TOKEN = '/oauth/token'
# obatin auth code
# GET
API_OAUTH_AUTHORIZE = '/oauth/authorize'
# revoke token
# POST
API_REVOKE_TOKEN = '/oauth/revoke'
# relationships
# GET
API_GET_RELATIONSHIPS = '/api/v1/accounts/relationships'
# toot
# POST
API_PUBLISH_TOOT = '/api/v1/statuses'
get = functools.partial(requests.get, timeout=MASTODON_TIMEOUT)
post = functools.partial(requests.post, timeout=MASTODON_TIMEOUT)
def get_relationships(id_list, token):
url = 'https://' + MASTODON_DOMAIN_NAME + API_GET_RELATIONSHIPS
payload = {'id[]': id_list}
headers = {
'Authorization': f'Bearer {token}'
}
response = get(url, headers=headers, data=payload)
return response.json()
def check_visibility(user_owned_entity, token, visitor):
"""
check if given user can see the user owned entity
"""
if not visitor == user_owned_entity.owner:
# mastodon request
relationship = get_relationships([user_owned_entity.owner.mastodon_id], token)[0]
if relationship['blocked_by']:
return False
if not relationship['following'] and user_owned_entity.is_private:
return False
return True
else:
return True
def post_toot(content, visibility, token, local_only=False):
url = 'https://' + MASTODON_DOMAIN_NAME + API_PUBLISH_TOOT
headers = {
'Authorization': f'Bearer {token}',
'Idempotency-Key': random_string_generator(16)
}
payload = {
'status': content,
'visibility': visibility,
'local_only': True,
}
if not local_only:
del payload['local_only']
response = post(url, headers=headers, data=payload)
return response
def random_string_generator(n):
s = string.ascii_letters + string.punctuation + string.digits
return ''.join(random.choice(s) for i in range(n))
class TootVisibilityEnum:
PUBLIC = 'public'
PRIVATE = 'private'
DIRECT = 'direct'
UNLISTED = 'unlisted'

View file

@ -6,7 +6,7 @@ from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q
from markdownx.models import MarkdownxField
from users.models import User
from common.mastodon.api import get_relationships
from mastodon.api import get_relationships, get_cross_site_id
# abstract base classes
@ -15,24 +15,30 @@ class Resource(models.Model):
rating_total_score = models.PositiveIntegerField(null=True, blank=True)
rating_number = models.PositiveIntegerField(null=True, blank=True)
rating = models.DecimalField(null=True, blank=True, max_digits=3, decimal_places=1)
rating = models.DecimalField(
null=True, blank=True, max_digits=3, decimal_places=1)
created_time = models.DateTimeField(auto_now_add=True)
edited_time = models.DateTimeField(auto_now_add=True)
last_editor = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='%(class)s_last_editor', null=True, blank=False)
last_editor = models.ForeignKey(
User, on_delete=models.SET_NULL, related_name='%(class)s_last_editor', null=True, blank=False)
brief = models.TextField(blank=True, default="")
other_info = postgres.JSONField(blank=True, null=True, encoder=DjangoJSONEncoder, default=dict)
other_info = postgres.JSONField(
blank=True, null=True, encoder=DjangoJSONEncoder, default=dict)
class Meta:
abstract = True
constraints = [
models.CheckConstraint(check=models.Q(rating__gte=0), name='%(class)s_rating_lowerbound'),
models.CheckConstraint(check=models.Q(rating__lte=10), name='%(class)s_rating_upperbound'),
]
models.CheckConstraint(check=models.Q(
rating__gte=0), name='%(class)s_rating_lowerbound'),
models.CheckConstraint(check=models.Q(
rating__lte=10), name='%(class)s_rating_upperbound'),
]
def save(self, *args, **kwargs):
""" update rating before save to db """
if self.rating_number and self.rating_total_score:
self.rating = Decimal(str(round(self.rating_total_score / self.rating_number, 1)))
self.rating = Decimal(
str(round(self.rating_total_score / self.rating_number, 1)))
elif self.rating_number is None and self.rating_total_score is None:
self.rating = None
else:
@ -40,9 +46,9 @@ class Resource(models.Model):
super().save(*args, **kwargs)
def calculate_rating(self, old_rating, new_rating):
if (not (self.rating and self.rating_total_score and self.rating_number)\
and (self.rating or self.rating_total_score or self.rating_number))\
or (not (self.rating or self.rating_number or self.rating_total_score) and old_rating is not None):
if (not (self.rating and self.rating_total_score and self.rating_number)
and (self.rating or self.rating_total_score or self.rating_number))\
or (not (self.rating or self.rating_number or self.rating_total_score) and old_rating is not None):
raise IntegrityError("Rating integiry error.")
if old_rating:
if new_rating:
@ -71,7 +77,7 @@ class Resource(models.Model):
else:
# none -> none
pass
def update_rating(self, old_rating, new_rating):
self.calculate_rating(old_rating, new_rating)
self.save()
@ -89,7 +95,7 @@ class Resource(models.Model):
There is no ocassion where visitor can simply view all the marks.
"""
raise NotImplementedError("Subclass should implement this method.")
def get_revies_manager(self):
"""
Normally this won't be used.
@ -115,7 +121,8 @@ class Resource(models.Model):
class UserOwnedEntity(models.Model):
is_private = models.BooleanField()
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_%(class)ss')
owner = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='user_%(class)ss')
created_time = models.DateTimeField(auto_now_add=True)
edited_time = models.DateTimeField(auto_now_add=True)
@ -123,33 +130,48 @@ class UserOwnedEntity(models.Model):
abstract = True
@classmethod
def get_available(cls, resource, user, token):
def get_available(cls, resource, request_user, token):
# TODO add amount limit for once query
"""
Returns all avaliable user-owned entities related to given resource.
This method handles mute/block relationships and private/public visibilities.
"""
# the foreign key field that points to resource
# the foreign key field that points to resource
# has to be named as the lower case name of that resource
query_kwargs = {resource.__class__.__name__.lower(): resource}
user_owned_entities = cls.objects.filter(**query_kwargs).order_by("-edited_time")
user_owned_entities = cls.objects.filter(
**query_kwargs).order_by("-edited_time")
# every user should only be abled to have one user owned entity for each resource
# this is guaranteed by models
id_list = [e.owner.mastodon_id for e in user_owned_entities]
# Mastodon request
relationships = get_relationships(id_list, token)
mute_block_blocked = []
following = []
for r in relationships:
# check json data type
if r['blocking'] or r['blocked_by'] or r['muting']:
mute_block_blocked.append(r['id'])
if r['following']:
following.append(r['id'])
user_owned_entities = user_owned_entities.exclude(owner__mastodon_id__in=mute_block_blocked)
following.append(str(user.mastodon_id))
user_owned_entities = user_owned_entities.exclude(Q(is_private=True) & ~Q(owner__mastodon_id__in=following))
return user_owned_entities
id_list = []
for entity in user_owned_entities:
if entity.owner.mastodon_site == request_user.mastodon_site:
id_list.append(entity.owner.mastodon_id)
else:
# TODO there could be many requests therefore make the pulling asynchronized
id_list.append(get_cross_site_id(
entity.owner, request_user.mastodon_site, token))
# Mastodon request
relationships = get_relationships(
request_user.mastodon_site, id_list, token)
mute_block_blocked_index = []
following_index = []
for i, r in enumerate(relationships):
# the order of relationships is corresponding to the id_list,
# and the order of id_list is the same as user_owned_entiies
if r['blocking'] or r['blocked_by'] or r['muting']:
mute_block_blocked_index.append(i)
if r['following']:
following_index.append(i)
available_entities = [
e for i, e in enumerate(user_owned_entities)
if ((e.is_private == True and i in following_index) or e.is_private == False or e.owner == request_user)
and not i in mute_block_blocked_index
]
return available_entities
@classmethod
def get_available_user_data(cls, owner, is_following):
@ -184,8 +206,10 @@ class Mark(UserOwnedEntity):
class Meta:
abstract = True
constraints = [
models.CheckConstraint(check=models.Q(rating__gte=0), name='mark_rating_lowerbound'),
models.CheckConstraint(check=models.Q(rating__lte=10), name='mark_rating_upperbound'),
models.CheckConstraint(check=models.Q(
rating__gte=0), name='mark_rating_lowerbound'),
models.CheckConstraint(check=models.Q(
rating__lte=10), name='mark_rating_upperbound'),
]

View file

@ -597,6 +597,7 @@ select::placeholder {
.navbar .navbar__search-box > input[type="search"] {
height: 26px;
padding: 4px 6px;
width: 32vw;
}
.navbar .navbar__search-box .navbar__search-dropdown {
cursor: pointer;

1
common/static/css/boofilsic.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -3,6 +3,8 @@ $(document).ready( function() {
let token = $("#oauth2Token").text();
let mast_uri = $("#mastodonURI").text();
let mast_domain = new URL(mast_uri);
mast_domain = mast_domain.hostname;
let id = $("#userMastodonID").text();
let userInfoSpinner = $("#spinner").clone().removeAttr("hidden");
@ -56,7 +58,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
$(".mast-followers").append(temp);
});
@ -88,7 +95,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
$(".mast-following").append(temp);
});

View file

@ -111,6 +111,7 @@
& > input[type="search"]
height: $widget-height
padding: 4px 6px
width: 32vw
& .navbar__search-dropdown
cursor: pointer
height: $widget-height

View file

@ -22,7 +22,7 @@
<script src="{% static 'js/home.js' %}"></script>
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
@ -300,10 +300,16 @@
</div>
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
{% if user == request.user %}
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}?is_mastodon_id=true</div>
{% else %}
<div id="userMastodonID" hidden="true">{{ user.target_site_id }}</div>
{% endif %}
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}</div>
<div id="spinner" hidden>
<div class="spinner">

View file

@ -17,7 +17,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
@ -356,7 +356,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -26,7 +26,7 @@
<a class="navbar__link" id="logoutLink" href="{% url 'users:logout' %}">{% trans '登出' %}</a>
<a class="navbar__link" href="{% url 'common:home' %}">{% trans '主页' %}</a>
{% if user.is_staff %}
{% if request.user.is_staff %}
<a class="navbar__link" href="{% admin_url %}">{% trans '后台' %}</a>
{% endif %}
@ -41,11 +41,13 @@
</nav>
<script>
$("#searchInput").on('keyup', function (e) {
e.preventDefault();
if (e.keyCode === 13) {
let q = $(this).val();
let c = $("#searchCategory").val();
if (q) {
location.href = "{% url 'common:search' %}" + "?c=" + c + "&q=" + q;
let new_location = "{% url 'common:search' %}" + "?c=" + c + "&q=" + q;
setTimeout(function () { document.location.href = new_location; }, 150);
}
}
});

View file

@ -1,12 +1,12 @@
from django import template
from django.conf import settings
from django.utils.html import format_html
from django.template.defaultfilters import stringfilter
register = template.Library()
@register.simple_tag
def mastodon():
url = 'https://' + settings.MASTODON_DOMAIN_NAME
return format_html(url)
def mastodon(domain):
url = 'https://' + domain
return url

View file

@ -6,7 +6,7 @@ register = template.Library()
@register.filter(is_safe=True)
@stringfilter
def strip_protocol(value):
def strip_scheme(value):
""" Strip the `https://.../` part of urls"""
if value.startswith("https://"):
value = value.replace("https://", '')

59
mastodon/admin.py Normal file
View file

@ -0,0 +1,59 @@
from django.contrib import admin
from .models import *
from .api import create_app
from django.utils.translation import gettext_lazy as _
from requests.exceptions import Timeout
from django.core.exceptions import ObjectDoesNotExist
# Register your models here.
@admin.register(MastodonApplication)
class MastodonApplicationModelAdmin(admin.ModelAdmin):
def add_view(self, request, form_url='', extra_context=None):
"""
Dirty code here, use POST['domain_name'] to pass error message to user.
"""
if request.method == 'POST':
if not request.POST.get('client_id') and not request.POST.get('client_secret'):
# make the post data mutable
request.POST = request.POST.copy()
# (is_proxy xor proxy_to) or (proxy_to!=null and is_proxy=false)
if (bool(request.POST.get('is_proxy')) or bool(request.POST.get('proxy_to'))) and\
not (bool(request.POST.get('is_proxy')) and bool(request.POST.get('proxy_to'))) or\
(not bool(request.POST.get('is_proxy')) and bool(request.POST.get('proxy_to'))):
request.POST['domain_name'] = _("请同时填写is_proxy和proxy_to。")
else:
if request.POST['is_proxy']:
try:
origin = MastodonApplication.objects.get(domain_name=request.POST['proxy_to'])
# set proxy credentials to those of its original site
request.POST['app_id'] = origin.app_id
request.POST['client_id'] = origin.client_id
request.POST['client_secret'] = origin.client_secret
request.POST['vapid_key'] = origin.vapid_key
except ObjectDoesNotExist:
request.POST['domain_name'] = _("proxy_to所指域名不存在请先添加原站点。")
else:
# create mastodon app
try:
response = create_app(request.POST.get('domain_name'))
except (Timeout, ConnectionError):
request.POST['domain_name'] = _("长毛象请求超时。")
except Exception as e:
request.POST['domain_name'] = str(e)
else:
# fill the form with returned data
data = response.json()
if response.status_code != 200:
request.POST['domain_name'] = str(data)
else:
request.POST['app_id'] = data['id']
request.POST['client_id'] = data['client_id']
request.POST['client_secret'] = data['client_secret']
request.POST['vapid_key'] = data['vapid_key']
return super().add_view(request, form_url=form_url, extra_context=extra_context)
admin.site.register(CrossSiteUserInfo)

193
mastodon/api.py Normal file
View file

@ -0,0 +1,193 @@
import requests
import string
import random
import functools
from django.core.exceptions import ObjectDoesNotExist
from boofilsic.settings import MASTODON_TIMEOUT
from boofilsic.settings import CLIENT_NAME, APP_WEBSITE
from .models import CrossSiteUserInfo
# See https://docs.joinmastodon.org/methods/accounts/
# returns user info
# retruns the same info as verify account credentials
# GET
API_GET_ACCOUNT = '/api/v1/accounts/:id'
# returns user info if valid, 401 if invalid
# GET
API_VERIFY_ACCOUNT = '/api/v1/accounts/verify_credentials'
# obtain token
# GET
API_OBTAIN_TOKEN = '/oauth/token'
# obatin auth code
# GET
API_OAUTH_AUTHORIZE = '/oauth/authorize'
# revoke token
# POST
API_REVOKE_TOKEN = '/oauth/revoke'
# relationships
# GET
API_GET_RELATIONSHIPS = '/api/v1/accounts/relationships'
# toot
# POST
API_PUBLISH_TOOT = '/api/v1/statuses'
# create new app
# POST
API_CREATE_APP = '/api/v1/apps'
# search
# GET
API_SEARCH = '/api/v2/search'
get = functools.partial(requests.get, timeout=MASTODON_TIMEOUT)
post = functools.partial(requests.post, timeout=MASTODON_TIMEOUT)
# low level api below
def get_relationships(site, id_list, token):
url = 'https://' + site + API_GET_RELATIONSHIPS
payload = {'id[]': id_list}
headers = {
'Authorization': f'Bearer {token}'
}
response = get(url, headers=headers, data=payload)
return response.json()
def post_toot(site, content, visibility, token, local_only=False):
url = 'https://' + site + API_PUBLISH_TOOT
headers = {
'Authorization': f'Bearer {token}',
'Idempotency-Key': random_string_generator(16)
}
payload = {
'status': content,
'visibility': visibility,
'local_only': True,
}
if not local_only:
del payload['local_only']
response = post(url, headers=headers, data=payload)
return response
def create_app(domain_name):
# naive protocal strip
is_http = False
if domain_name.startswith("https://"):
domain_name = domain_name.replace("https://", '')
elif domain_name.startswith("http://"):
is_http = True
domain_name = domain_name.replace("http://", '')
if domain_name.endswith('/'):
domain_name = domain_name[0:-1]
if not is_http:
url = 'https://' + domain_name + API_CREATE_APP
else:
url = 'http://' + domain_name + API_CREATE_APP
payload = {
'client_name': CLIENT_NAME,
'scopes': 'read write follow',
'redirect_uris': f'{APP_WEBSITE}/users/OAuth2_login/',
'website': APP_WEBSITE
}
from boofilsic.settings import DEBUG
if DEBUG:
payload['redirect_uris'] = 'http://localhost/users/OAuth2_login/'
response = post(url, data=payload)
return response
def get_site_id(username, site, token):
url = 'https://' + site + API_SEARCH
payload = {
'limit': 1,
'type': 'accounts',
'q': f"{username}@{site}"
}
headers = {
'Authorization': f'Bearer {token}'
}
response = get(url, data=payload, headers=headers)
data = response.json()
if not data['accounts']:
return None
else:
return data['accounts'][0]['id']
# high level api below
def get_relationship(request_user, target_user, token):
if request_user.mastodon_site == target_user.mastodon_site:
return get_relationships(request_user.mastodon_site, target_user.mastodon_id, token)
else:
cross_site_id = get_cross_site_id(target_user, request_user.mastodon_site, token)
return get_relationships(request_user.mastodon_site, [cross_site_id,], token)
def get_cross_site_id(target_user, target_site, token):
"""
Firstly attempt to query local database, if the cross site id
doesn't exsit then make a query to mastodon site, then save the
result into database.
Return target_user at target_site cross site id.
"""
if target_site == target_user.mastodon_site:
return target_user.mastodon_id
try:
cross_site_info = CrossSiteUserInfo.objects.get(
uid=f"{target_user.username}@{target_user.mastodon_site}",
target_site=target_site
)
except ObjectDoesNotExist:
cross_site_id = get_site_id(
target_user.username, target_site, token)
cross_site_info = CrossSiteUserInfo.objects.create(
uid=f"{target_user.username}@{target_user.mastodon_site}",
target_site=target_site,
site_id=cross_site_id,
local_id=target_user.id
)
return cross_site_info.site_id
def check_visibility(user_owned_entity, token, visitor):
"""
check if given user can see the user owned entity
"""
if not visitor == user_owned_entity.owner:
# mastodon request
relationship = get_relationship(visitor, user_owned_entity.owner, token)[0]
if relationship['blocked_by']:
return False
if not relationship['following'] and user_owned_entity.is_private:
return False
return True
else:
return True
# utils below
def random_string_generator(n):
s = string.ascii_letters + string.punctuation + string.digits
return ''.join(random.choice(s) for i in range(n))
class TootVisibilityEnum:
PUBLIC = 'public'
PRIVATE = 'private'
DIRECT = 'direct'
UNLISTED = 'unlisted'

5
mastodon/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CommonConfig(AppConfig):
name = 'mastodon'

View file

@ -1,14 +1,15 @@
from django.contrib.auth.backends import ModelBackend, UserModel
from django.shortcuts import reverse
from boofilsic.settings import MASTODON_DOMAIN_NAME, CLIENT_ID, CLIENT_SECRET
from .api import *
from .models import MastodonApplication
def obtain_token(request, code):
def obtain_token(site, request, code):
""" Returns token if success else None. """
mast_app = MastodonApplication.objects.get(domain_name=site)
payload = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'client_id': mast_app.client_id,
'client_secret': mast_app.client_secret,
'redirect_uri': f"https://{request.get_host()}{reverse('users:OAuth2_login')}",
'grant_type': 'authorization_code',
'code': code,
@ -17,7 +18,10 @@ def obtain_token(request, code):
from boofilsic.settings import DEBUG
if DEBUG:
payload['redirect_uri']= f"http://{request.get_host()}{reverse('users:OAuth2_login')}",
url = 'https://' + MASTODON_DOMAIN_NAME + API_OBTAIN_TOKEN
if mast_app.is_proxy:
url = 'https://' + mast_app.proxy_to + API_OBTAIN_TOKEN
else:
url = 'https://' + mast_app.domain_name + API_OBTAIN_TOKEN
response = post(url, data=payload)
if response.status_code != 200:
return
@ -25,8 +29,8 @@ def obtain_token(request, code):
return data.get('access_token')
def get_user_data(token):
url = 'https://' + MASTODON_DOMAIN_NAME + API_VERIFY_ACCOUNT
def get_user_data(site, token):
url = 'https://' + site + API_VERIFY_ACCOUNT
headers = {
'Authorization': f'Bearer {token}'
}
@ -36,19 +40,25 @@ def get_user_data(token):
return response.json()
def revoke_token(token):
def revoke_token(site, token):
mast_app = MastodonApplication.objects.get(domain_name=site)
payload = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'client_id': mast_app.client_id,
'client_secret': mast_app.client_secret,
'scope': token
}
url = 'https://' + MASTODON_DOMAIN_NAME + API_REVOKE_TOKEN
if mast_app.is_proxy:
url = 'https://' + mast_app.proxy_to + API_REVOKE_TOKEN
else:
url = 'https://' + site + API_REVOKE_TOKEN
response = post(url, data=payload)
def verify_token(token):
def verify_token(site, token):
""" Check if the token is valid and is of local instance. """
url = 'https://' + MASTODON_DOMAIN_NAME + API_VERIFY_ACCOUNT
url = 'https://' + site + API_VERIFY_ACCOUNT
headers = {
'Authorization': f'Bearer {token}'
}
@ -66,13 +76,13 @@ class OAuth2Backend(ModelBackend):
# "authenticate() should check the credentials it gets and returns
# a user object that matches those credentials."
# arg request is an interface specification, not used in this implementation
def authenticate(self, request, token=None, username=None, **kwargs):
def authenticate(self, request, token=None, username=None, site=None, **kwargs):
""" when username is provided, assume that token is newly obtained and valid """
if token is None:
if token is None or site is None:
return
if username is None:
user_data = get_user_data(token)
user_data = get_user_data(site, token)
if user_data:
username = user_data['username']
else:

View file

@ -11,15 +11,7 @@ def mastodon_request_included(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Timeout:
return render(
args[0],
'common/error.html',
{
'msg': _("长毛象请求超时叻_(´ཀ`」 ∠)__ ")
}
)
except ConnectionError:
except (Timeout, ConnectionError):
return render(
args[0],
'common/error.html',

36
mastodon/models.py Normal file
View file

@ -0,0 +1,36 @@
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
class MastodonApplication(models.Model):
domain_name = models.CharField(_('site domain name'), max_length=100, unique=True)
app_id = models.PositiveIntegerField(_('in-site app id'))
client_id = models.CharField(_('client id'), max_length=100)
client_secret = models.CharField(_('client secret'), max_length=100)
vapid_key = models.CharField(_('vapid key'), max_length=200)
is_proxy = models.BooleanField(default=False, blank=True)
proxy_to = models.CharField(max_length=100, blank=True, default='')
# website
# name
# redirect_uris
def __str__(self):
return self.domain_name
class CrossSiteUserInfo(models.Model):
# username@original_site
uid = models.CharField(_("username and original site"), max_length=200)
# pk in the boofilsic db
local_id = models.PositiveIntegerField(_("local database id"))
# target site domain name
target_site = models.CharField(_("target site domain name"), max_length=100)
# target site id
site_id = models.PositiveIntegerField()
class Meta:
constraints = [
models.UniqueConstraint(
fields=['uid', 'target_site'], name="unique_cross_site_user_info")
]

View file

@ -164,7 +164,10 @@ class Movie(Resource):
def __str__(self):
return self.title
if self.year:
return self.title + f"{self.year}"
else:
return self.title
def get_tags_manager(self):

View file

@ -14,7 +14,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
<body>
@ -57,7 +57,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -17,7 +17,7 @@
<script src="{% static 'js/create_update_review.js' %}"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>
@ -159,7 +159,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -19,7 +19,7 @@
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
<body>
@ -87,7 +87,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -14,7 +14,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
</head>
@ -94,7 +94,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -6,7 +6,7 @@
{% load mastodon %}
{% load oauth_token %}
{% load truncate %}
{% load strip_protocol %}
{% load strip_scheme %}
<!DOCTYPE html>
<html lang="en">
@ -41,7 +41,7 @@
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_modal.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
</head>
@ -183,7 +183,7 @@
{% endfor %}
{% endif %}</div>
<div>{% if movie.site %}{% trans '网站:' %}
<a href="{{ movie.site }}" target="_blank">{{ movie.site|strip_protocol }}</a>
<a href="{{ movie.site }}" target="_blank">{{ movie.site|strip_scheme }}</a>
{% endif %}</div>
{% if movie.other_info %}
{% for k, v in movie.other_info.items %}

View file

@ -17,7 +17,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
@ -170,7 +170,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -24,7 +24,7 @@
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
<body>
@ -150,7 +150,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -18,7 +18,7 @@
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>
@ -157,7 +157,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -13,7 +13,7 @@
<title>{% trans 'NiceDB - 从豆瓣获取数据' %}</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'js/scrape.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>

View file

@ -9,9 +9,9 @@ from django.db.models import Count
from django.utils import timezone
from django.core.paginator import Paginator
from django.core.files.uploadedfile import SimpleUploadedFile
from common.mastodon import mastodon_request_included
from common.mastodon.api import check_visibility, post_toot, TootVisibilityEnum
from common.mastodon.utils import rating_to_emoji
from mastodon import mastodon_request_included
from mastodon.api import check_visibility, post_toot, TootVisibilityEnum
from mastodon.utils import rating_to_emoji
from common.utils import PageLinksGenerator
from common.views import PAGE_LINK_NUMBER
from .models import *

View file

@ -17,7 +17,15 @@ def report_image_path(instance, filename):
class User(AbstractUser):
mastodon_id = models.IntegerField(unique=True)
mastodon_id = models.IntegerField(blank=False)
# mastodon domain name, eg donotban.com
mastodon_site = models.CharField(max_length=100, blank=False)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['username', 'mastodon_site'], name="unique_user_identity")
]
def save(self, *args, **kwargs):
""" Automatically populate password field with DEFAULT_PASSWORD before saving."""

View file

@ -3,6 +3,8 @@ $(document).ready( function() {
let token = $("#oauth2Token").text();
let mast_uri = $("#mastodonURI").text();
let mast_domain = new URL(mast_uri);
mast_domain = mast_domain.hostname;
let id = $("#userMastodonID").text();
let nextUrl = null;
let requesting = false;
@ -52,6 +54,8 @@ $(document).ready( function() {
if (userList.length > 4){
subUserList = userList.slice(0, 4);
$(".mast-followers-more").show();
} else {
subUserList = userList;
}
let template = $(".mast-followers li").clone();
$(".mast-followers").html("");
@ -63,7 +67,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
$(".mast-followers").append(temp);
});
@ -80,7 +89,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
temp.find(".mast-brief").text(data.note.replace(/(<([^>]+)>)/ig,""));
$(".mast-user:last").after(temp);
@ -119,7 +133,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
$(".mast-following").append(temp);
});

View file

@ -3,6 +3,8 @@ $(document).ready( function() {
let token = $("#oauth2Token").text();
let mast_uri = $("#mastodonURI").text();
let mast_domain = new URL(mast_uri);
mast_domain = mast_domain.hostname;
let id = $("#userMastodonID").text();
let nextUrl = null;
let requesting = false;
@ -62,7 +64,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
$(".mast-followers").append(temp);
});
@ -85,6 +92,8 @@ $(document).ready( function() {
if (userList.length > 4){
subUserList = userList.slice(0, 4);
$(".mast-following-more").show();
} else {
subUserList = userList;
}
let template = $(".mast-following li").clone();
$(".mast-following").html("");
@ -96,7 +105,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
$(".mast-following").append(temp);
});
@ -113,7 +127,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
temp.find(".mast-brief").text(data.note.replace(/(<([^>]+)>)/ig,""));
$(".mast-user:last").after(temp);
@ -173,7 +192,12 @@ $(document).ready( function() {
} else {
temp.find(".mast-displayname").text(data.username);
}
let url = $("#userPageURL").text().replace('0', data.id);
let url;
if (data.acct.includes('@')) {
url = $("#userPageURL").text().replace('0', data.acct);
} else {
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
}
temp.find("a").attr('href', url);
temp.find(".mast-brief").text($(data.note).text());
// console.log($(temp).html())

11
users/static/lib/css/chosen.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
users/static/lib/js/js.cookie.min.js vendored Normal file
View file

@ -0,0 +1,2 @@
/*! js-cookie v3.0.0-rc.1 | MIT */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self,function(){var n=e.Cookies,r=e.Cookies=t();r.noConflict=function(){return e.Cookies=n,r}}())}(this,function(){"use strict";function e(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)e[r]=n[r]}return e}var t={read:function(e){return e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};return function n(r,o){function i(t,n,i){if("undefined"!=typeof document){"number"==typeof(i=e({},o,i)).expires&&(i.expires=new Date(Date.now()+864e5*i.expires)),i.expires&&(i.expires=i.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape),n=r.write(n,t);var c="";for(var u in i)i[u]&&(c+="; "+u,!0!==i[u]&&(c+="="+i[u].split(";")[0]));return document.cookie=t+"="+n+c}}return Object.create({set:i,get:function(e){if("undefined"!=typeof document&&(!arguments.length||e)){for(var n=document.cookie?document.cookie.split("; "):[],o={},i=0;i<n.length;i++){var c=n[i].split("="),u=c.slice(1).join("=");'"'===u[0]&&(u=u.slice(1,-1));try{var f=t.read(c[0]);if(o[f]=r.read(u,f),e===f)break}catch(e){}}return e?o[e]:o}},remove:function(t,n){i(t,"",e({},n,{expires:-1}))},withAttributes:function(t){return n(this.converter,e({},this.attributes,t))},withConverter:function(t){return n(e({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(o)},converter:{value:Object.freeze(r)}})}(t,{path:"/"})});

View file

@ -18,7 +18,7 @@
<script src="{% static 'js/mastodon.js' %}"></script>
<script src="{% static 'js/home.js' %}"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>
@ -248,10 +248,14 @@
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
{% if user == request.user %}
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}?is_mastodon_id=true</div>
{% else %}
<div id="userMastodonID" hidden="true">{{ user.target_site_id }}</div>
{% endif %}
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}</div>
<div id="spinner" hidden>
<div class="spinner">
<div></div>

View file

@ -13,26 +13,58 @@
<link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic_box.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/chosen.min.css' %}"> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- <script src="{% static 'lib/js/chosen.jquery.min.js' %}"></script> -->
<script src="{% static 'lib/js/js.cookie.min.js' %}"></script>
<title>{% trans 'NiceDB - 登录' %}</title>
</head>
<body>
<style>
select {
padding-left: 16px;
padding-right: 16px;
margin-bottom: 20px;;
}
</style>
<div id="loginBox" class="box">
<img src="{% static 'img/logo.svg' %}" class="logo" alt="boofilsic logo">
<div id="loginButton">
{% if user.is_authenticated %}
<a href="{% url 'common:home' %}" class="button">{% trans '前往我的主页' %}</a>
{% else %}
<a href="{{ oauth_url }}" class="button">{% trans '使用长毛象授权登录' %}</a>
<br>
<a href="{{ proxy_site_oauth_url }}" class="button">{% trans '礼貌站登录入口' %}</a>
<a href="{% url 'common:home' %}" class="button">{% trans '前往我的主页' %}</a>
{% else %}
<select name="sites" id="sitesSelect">
{% for site in sites %}
<option value="{{ site.domain_name }}" data-client-id="{{ site.client_id }}">@{{ site.domain_name }}</option>
{% endfor %}
</select>
<button name='login'>{% trans '授权登录' %}</button>
{% endif %}
</div>
</div>
{% if not user.is_authenticated %}
<script>
// $("#sitesSelect").chosen();
$("#sitesSelect").val("{{ selected_site }}");
$("button[name=login]").click(function(e) {
e.preventDefault();
let selected = $("#sitesSelect").find(":selected");
let client_id = selected.data("client-id");
let domain = selected.val();
Cookies.set('mastodon_domain', domain);
location.href = "https://" + domain + "/oauth/authorize?client_id=" + client_id +
"&scope=read+write&redirect_uri=https://{{ request.get_host }}{% url 'users:OAuth2_login' %}" +
"&response_type=code";
});
</script>
{% endif %}
</body>
</html>

View file

@ -13,7 +13,7 @@
<title>{% trans 'NiceDB - 管理举报' %}</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'js/create_update.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
</head>
@ -53,7 +53,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -20,7 +20,7 @@
<script src="{% static 'js/mastodon.js' %}"></script>
<script src="{% static 'js/home.js' %}"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>
@ -252,10 +252,14 @@
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
{% if user == request.user %}
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}?is_mastodon_id=true</div>
{% else %}
<div id="userMastodonID" hidden="true">{{ user.target_site_id }}</div>
{% endif %}
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}</div>
<div id="spinner" hidden>
<div class="spinner">
<div></div>

View file

@ -18,10 +18,10 @@
<img src="{% static 'img/logo.svg' %}" class="logo" alt="boofilsic logo">
<div id="loginButton">
<p>欢迎来到里瓣书影音(其实现在只有书汗电影)!</p>
<p>欢迎来到NiceDB书影音(其实现在只有书汗电影)!</p>
<p>
里瓣书影音继承了长毛象的用户关系比如您在里瓣屏蔽了某人那您将不会在书影音的公共区域看到TA的痕迹。
这里仍是一片处女地,丰富的内容需要大家共同创造
NiceDB书影音继承了长毛象的用户关系比如您在里瓣屏蔽了某人那您将不会在书影音的公共区域看到TA的痕迹。
这里仍是一片处女地,丰富的内容需要大家共同创造
请注意虽然您可以随意发表任何言论,但试图添加垃圾数据到公共数据领域(如添加不存在的乱码的书籍)将会受到制裁!
BTW欧盟惯例本站使用了Cookie请理解
</p>

View file

@ -25,7 +25,7 @@
{% endif %}
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_browse.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
</head>
<body>
@ -136,10 +136,14 @@
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
{% if user == request.user %}
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}?is_mastodon_id=true</div>
{% else %}
<div id="userMastodonID" hidden="true">{{ user.target_site_id }}</div>
{% endif %}
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}</div>
<div id="spinner" hidden>
<div class="spinner">
<div></div>

View file

@ -13,7 +13,7 @@
<title>{% trans 'NiceDB - 举报用户' %}</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'js/create_update.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}">
<link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
</head>
@ -42,7 +42,7 @@
{% comment %}
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
<div id="mastodonURI" hidden="true">{% mastodon %}</div>
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
<!--current user mastodon id-->
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
{% endcomment %}

View file

@ -13,6 +13,11 @@ urlpatterns = [
path('<int:id>/following/', following, name='following'),
path('<int:id>/book/<str:status>/', book_list, name='book_list'),
path('<int:id>/movie/<str:status>/', movie_list, name='movie_list'),
path('<str:id>/', home, name='home'),
path('<str:id>/followers/', followers, name='followers'),
path('<str:id>/following/', following, name='following'),
path('<str:id>/book/<str:status>/', book_list, name='book_list'),
path('<str:id>/movie/<str:status>/', movie_list, name='movie_list'),
path('report/', report, name='report'),
path('manage_report/', manage_report, name='manage_report'),
]

View file

@ -9,17 +9,17 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Count
from .models import User, Report
from .forms import ReportForm
from common.mastodon.auth import *
from common.mastodon.api import *
from common.mastodon import mastodon_request_included
from mastodon.auth import *
from mastodon.api import *
from mastodon import mastodon_request_included
from common.views import BOOKS_PER_SET, ITEMS_PER_PAGE, PAGE_LINK_NUMBER, TAG_NUMBER_ON_LIST, MOVIES_PER_SET
from common.models import MarkStatusEnum
from common.utils import PageLinksGenerator
from books.models import *
from movies.models import *
from boofilsic.settings import MASTODON_DOMAIN_NAME, CLIENT_ID, CLIENT_SECRET
from books.forms import BookMarkStatusTranslator
from movies.forms import MovieMarkStatusTranslator
from mastodon.models import MastodonApplication
# Views
@ -31,14 +31,18 @@ def OAuth2_login(request):
""" oauth authentication and logging user into django system """
if request.method == 'GET':
code = request.GET.get('code')
site = request.COOKIES.get('mastodon_domain')
# Network IO
token = obtain_token(request, code)
token = obtain_token(site, request, code)
if token:
# oauth is completed when token aquired
user = authenticate(request, token=token)
user = authenticate(request, token=token, site=site)
if user:
auth_login(request, user, token)
return redirect(reverse('common:home'))
response = redirect(reverse('common:home'))
response.delete_cookie('mastodon_domain')
return response
else:
# will be passed to register page
request.session['new_user_token'] = token
@ -58,29 +62,16 @@ def OAuth2_login(request):
# the 'login' page that user can see
def login(request):
if request.method == 'GET':
auth_url = f"https://{MASTODON_DOMAIN_NAME}{API_OAUTH_AUTHORIZE}?" +\
f"client_id={CLIENT_ID}&scope=read+write&" +\
f"redirect_uri=https://{request.get_host()}{reverse('users:OAuth2_login')}" +\
"&response_type=code"
selected_site = request.GET.get('site', default='')
proxy_site_auth_url = f"https://pleasedonotban.com{API_OAUTH_AUTHORIZE}?" +\
f"client_id={CLIENT_ID}&scope=read+write&" +\
f"redirect_uri=https://{request.get_host()}{reverse('users:OAuth2_login')}" +\
"&response_type=code"
from boofilsic.settings import DEBUG
if DEBUG:
auth_url = f"https://{MASTODON_DOMAIN_NAME}{API_OAUTH_AUTHORIZE}?" +\
f"client_id={CLIENT_ID}&scope=read+write&" +\
f"redirect_uri=http://{request.get_host()}{reverse('users:OAuth2_login')}" +\
"&response_type=code"
sites = MastodonApplication.objects.all().order_by("domain_name")
return render(
request,
'users/login.html',
{
'oauth_url': auth_url,
'proxy_site_oauth_url': proxy_site_auth_url
'sites': sites,
'selected_site': selected_site,
}
)
else:
@ -91,7 +82,7 @@ def login(request):
@login_required
def logout(request):
if request.method == 'GET':
revoke_token(request.session['oauth_token'])
revoke_token(request.user.mastodon_site, request.session['oauth_token'])
auth_logout(request)
return redirect(reverse("users:login"))
else:
@ -113,15 +104,18 @@ def register(request):
return HttpResponseBadRequest()
elif request.method == 'POST':
token = request.session['new_user_token']
user_data = get_user_data(token)
user_data = get_user_data(request.COOKIES['mastodon_domain'], token)
new_user = User(
username=user_data['username'],
mastodon_id=user_data['id']
mastodon_id=user_data['id'],
mastodon_site=request.COOKIES['mastodon_domain'],
)
new_user.save()
del request.session['new_user_token']
auth_login(request, new_user, token)
return redirect(reverse('common:home'))
response = redirect(reverse('common:home'))
response.delete_cookie('mastodon_domain')
return response
else:
return HttpResponseBadRequest()
@ -134,9 +128,14 @@ def delete(request):
@login_required
def home(request, id):
if request.method == 'GET':
if request.GET.get('is_mastodon_id', '').lower() == 'true':
query_kwargs = {'mastodon_id': id}
else:
if isinstance(id, str):
try:
username = id.split('@')[0]
site = id.split('@')[1]
except IndexError as e:
return HttpResponseBadRequest("Invalid user id")
query_kwargs = {'username': username, 'mastodon_site': site}
elif isinstance(id, int):
query_kwargs = {'pk': id}
try:
user = User.objects.get(**query_kwargs)
@ -155,7 +154,7 @@ def home(request, id):
return redirect("common:home")
else:
# mastodon request
relation = get_relationships([user.mastodon_id], request.session['oauth_token'])[0]
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
if relation['blocked_by']:
msg = _("你没有访问TA主页的权限😥")
return render(
@ -185,6 +184,9 @@ def home(request, id):
collect_movie_marks = movie_marks.filter(status=MarkStatusEnum.COLLECT)
collect_movies_more = True if collect_movie_marks.count() > BOOKS_PER_SET else False
user.target_site_id = get_cross_site_id(
user, request.user.mastodon_site, request.session['oauth_token'])
return render(
request,
'common/home.html',
@ -201,7 +203,7 @@ def home(request, id):
'collect_movie_marks': collect_movie_marks[:MOVIES_PER_SET],
'do_movies_more': do_movies_more,
'wish_movies_more': wish_movies_more,
'collect_movies_more': collect_movies_more,
'collect_movies_more': collect_movies_more,
}
)
else:
@ -212,8 +214,17 @@ def home(request, id):
@login_required
def followers(request, id):
if request.method == 'GET':
if isinstance(id, str):
try:
username = id.split('@')[0]
site = id.split('@')[1]
except IndexError as e:
return HttpResponseBadRequest("Invalid user id")
query_kwargs = {'username': username, 'mastodon_site': site}
elif isinstance(id, int):
query_kwargs = {'pk': id}
try:
user = User.objects.get(pk=id)
user = User.objects.get(**query_kwargs)
except ObjectDoesNotExist:
msg = _("😖哎呀这位老师还没有注册书影音呢快去长毛象喊TA来吧")
sec_msg = _("目前只开放本站用户注册")
@ -227,7 +238,7 @@ def followers(request, id):
)
# mastodon request
if not user == request.user:
relation = get_relationships([user.mastodon_id], request.session['oauth_token'])[0]
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
if relation['blocked_by']:
msg = _("你没有访问TA主页的权限😥")
return render(
@ -237,6 +248,8 @@ def followers(request, id):
'msg': msg,
}
)
user.target_site_id = get_cross_site_id(
user, request.user.mastodon_site, request.session['oauth_token'])
return render(
request,
'users/relation_list.html',
@ -253,8 +266,17 @@ def followers(request, id):
@login_required
def following(request, id):
if request.method == 'GET':
if isinstance(id, str):
try:
username = id.split('@')[0]
site = id.split('@')[1]
except IndexError as e:
return HttpResponseBadRequest("Invalid user id")
query_kwargs = {'username': username, 'mastodon_site': site}
elif isinstance(id, int):
query_kwargs = {'pk': id}
try:
user = User.objects.get(pk=id)
user = User.objects.get(**query_kwargs)
except ObjectDoesNotExist:
msg = _("😖哎呀这位老师还没有注册书影音呢快去长毛象喊TA来吧")
sec_msg = _("目前只开放本站用户注册")
@ -268,7 +290,7 @@ def following(request, id):
)
# mastodon request
if not user == request.user:
relation = get_relationships([user.mastodon_id], request.session['oauth_token'])[0]
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
if relation['blocked_by']:
msg = _("你没有访问TA主页的权限😥")
return render(
@ -278,6 +300,8 @@ def following(request, id):
'msg': msg,
}
)
user.target_site_id = get_cross_site_id(
user, request.user.mastodon_site, request.session['oauth_token'])
return render(
request,
'users/relation_list.html',
@ -296,8 +320,18 @@ def book_list(request, id, status):
if request.method == 'GET':
if not status.upper() in MarkStatusEnum.names:
return HttpResponseBadRequest()
if isinstance(id, str):
try:
username = id.split('@')[0]
site = id.split('@')[1]
except IndexError as e:
return HttpResponseBadRequest("Invalid user id")
query_kwargs = {'username': username, 'mastodon_site': site}
elif isinstance(id, int):
query_kwargs = {'pk': id}
try:
user = User.objects.get(pk=id)
user = User.objects.get(**query_kwargs)
except ObjectDoesNotExist:
msg = _("😖哎呀这位老师还没有注册书影音呢快去长毛象喊TA来吧")
sec_msg = _("目前只开放本站用户注册")
@ -311,7 +345,7 @@ def book_list(request, id, status):
)
if not user == request.user:
# mastodon request
relation = get_relationships([user.mastodon_id], request.session['oauth_token'])[0]
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
if relation['blocked_by']:
msg = _("你没有访问TA主页的权限😥")
return render(
@ -323,6 +357,8 @@ def book_list(request, id, status):
)
queryset = BookMark.get_available_user_data(user, relation['following']).filter(
status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
user.target_site_id = get_cross_site_id(
user, request.user.mastodon_site, request.session['oauth_token'])
else:
queryset = BookMark.objects.filter(
owner=user, status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
@ -353,8 +389,18 @@ def movie_list(request, id, status):
if request.method == 'GET':
if not status.upper() in MarkStatusEnum.names:
return HttpResponseBadRequest()
if isinstance(id, str):
try:
username = id.split('@')[0]
site = id.split('@')[1]
except IndexError as e:
return HttpResponseBadRequest("Invalid user id")
query_kwargs = {'username': username, 'mastodon_site': site}
elif isinstance(id, int):
query_kwargs = {'pk': id}
try:
user = User.objects.get(pk=id)
user = User.objects.get(**query_kwargs)
except ObjectDoesNotExist:
msg = _("😖哎呀这位老师还没有注册书影音呢快去长毛象喊TA来吧")
sec_msg = _("目前只开放本站用户注册")
@ -368,7 +414,7 @@ def movie_list(request, id, status):
)
if not user == request.user:
# mastodon request
relation = get_relationships([user.mastodon_id], request.session['oauth_token'])[0]
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
if relation['blocked_by']:
msg = _("你没有访问TA主页的权限😥")
return render(
@ -380,6 +426,8 @@ def movie_list(request, id, status):
)
queryset = MovieMark.get_available_user_data(user, relation['following']).filter(
status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
user.target_site_id = get_cross_site_id(
user, request.user.mastodon_site, request.session['oauth_token'])
else:
queryset = MovieMark.objects.filter(
owner=user, status=MarkStatusEnum[status.upper()]).order_by("-edited_time")