update cross site auth
This commit is contained in:
parent
77da5b8534
commit
e7f1fad210
56 changed files with 694 additions and 282 deletions
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
|
@ -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'
|
|
@ -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'),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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
1
common/static/css/boofilsic.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
& > input[type="search"]
|
||||
height: $widget-height
|
||||
padding: 4px 6px
|
||||
width: 32vw
|
||||
& .navbar__search-dropdown
|
||||
cursor: pointer
|
||||
height: $widget-height
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
59
mastodon/admin.py
Normal 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
193
mastodon/api.py
Normal 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
5
mastodon/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CommonConfig(AppConfig):
|
||||
name = 'mastodon'
|
|
@ -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:
|
|
@ -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
36
mastodon/models.py
Normal 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")
|
||||
]
|
|
@ -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):
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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
11
users/static/lib/css/chosen.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
3
users/static/lib/js/chosen.jquery.min.js
vendored
Normal file
3
users/static/lib/js/chosen.jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
users/static/lib/js/js.cookie.min.js
vendored
Normal file
2
users/static/lib/js/js.cookie.min.js
vendored
Normal 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:"/"})});
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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'),
|
||||
]
|
||||
|
|
130
users/views.py
130
users/views.py
|
@ -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")
|
||||
|
|
Loading…
Add table
Reference in a new issue