mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-24 09:00:33 +00:00
Merge pull request #151 from cthulahoops/style_fixes
Style fixes suggested by pylint.
This commit is contained in:
commit
3479185f67
26 changed files with 37 additions and 73 deletions
|
@ -49,4 +49,3 @@ def get_actor(user):
|
||||||
"url": icon_url,
|
"url": icon_url,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,3 @@ def get_follow_page(user_list, id_slug, page):
|
||||||
if start > 0:
|
if start > 0:
|
||||||
data['prev'] = '%s?page=%d' % (id_slug, page - 1)
|
data['prev'] = '%s?page=%d' % (id_slug, page - 1)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,3 @@ def get_outbox_page(user, page_id, statuses, max_id, min_id):
|
||||||
urlencode({'max_id': min_id, 'page': 'true'})
|
urlencode({'max_id': min_id, 'page': 'true'})
|
||||||
|
|
||||||
return page
|
return page
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,5 +31,3 @@ def get_add_remove(user, book, shelf, action='Add'):
|
||||||
'id': shelf.absolute_id,
|
'id': shelf.absolute_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -252,5 +252,3 @@ def get_remove_tag(tag):
|
||||||
'id': tag.book.absolute_id,
|
'id': tag.book.absolute_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
''' send out activitypub messages '''
|
''' send out activitypub messages '''
|
||||||
|
import json
|
||||||
|
from urllib.parse import urlparse
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Signature import pkcs1_15
|
from Crypto.Signature import pkcs1_15
|
||||||
from Crypto.Hash import SHA256
|
from Crypto.Hash import SHA256
|
||||||
from django.utils.http import http_date
|
from django.utils.http import http_date
|
||||||
import json
|
|
||||||
import requests
|
import requests
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from fedireads import models
|
from fedireads import models
|
||||||
from fedireads.tasks import app
|
from fedireads.tasks import app
|
||||||
|
@ -93,4 +93,3 @@ def sign_and_send(sender, activity, destination):
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
|
@ -33,34 +33,29 @@ class AbstractConnector(ABC):
|
||||||
def search(self, query):
|
def search(self, query):
|
||||||
''' free text search '''
|
''' free text search '''
|
||||||
# return list of search result objs
|
# return list of search result objs
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_or_create_book(self, book_id):
|
def get_or_create_book(self, book_id):
|
||||||
''' request and format a book given an identifier '''
|
''' request and format a book given an identifier '''
|
||||||
# return book model obj
|
# return book model obj
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def expand_book_data(self, book):
|
def expand_book_data(self, book):
|
||||||
''' get more info on a book '''
|
''' get more info on a book '''
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_or_create_author(self, book_id):
|
def get_or_create_author(self, book_id):
|
||||||
''' request and format a book given an identifier '''
|
''' request and format a book given an identifier '''
|
||||||
# return book model obj
|
# return book model obj
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def update_book(self, book_obj):
|
def update_book(self, book_obj):
|
||||||
''' sync a book with the canonical remote copy '''
|
''' sync a book with the canonical remote copy '''
|
||||||
# return book model obj
|
# return book model obj
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def update_from_mappings(obj, data, mappings):
|
def update_from_mappings(obj, data, mappings):
|
||||||
|
@ -96,7 +91,7 @@ def get_date(date_string):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class SearchResult(object):
|
class SearchResult:
|
||||||
''' standardized search result object '''
|
''' standardized search result object '''
|
||||||
def __init__(self, title, key, author, year, raw_data):
|
def __init__(self, title, key, author, year, raw_data):
|
||||||
self.title = title
|
self.title = title
|
||||||
|
@ -108,4 +103,3 @@ class SearchResult(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<SearchResult key={!r} title={!r} author={!r}>".format(
|
return "<SearchResult key={!r} title={!r} author={!r}>".format(
|
||||||
self.key, self.title, self.author)
|
self.key, self.title, self.author)
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,6 @@ from .abstract_connector import update_from_mappings, get_date
|
||||||
|
|
||||||
|
|
||||||
class Connector(AbstractConnector):
|
class Connector(AbstractConnector):
|
||||||
''' instantiate a connector '''
|
|
||||||
def __init__(self, identifier):
|
|
||||||
super().__init__(identifier)
|
|
||||||
|
|
||||||
|
|
||||||
def search(self, query):
|
def search(self, query):
|
||||||
''' right now you can't search fedireads, but... '''
|
''' right now you can't search fedireads, but... '''
|
||||||
resp = requests.get(
|
resp = requests.get(
|
||||||
|
@ -80,7 +75,7 @@ class Connector(AbstractConnector):
|
||||||
author_id = author_id.split('/')[-1]
|
author_id = author_id.split('/')[-1]
|
||||||
book.authors.add(self.get_or_create_author(author_id))
|
book.authors.add(self.get_or_create_author(author_id))
|
||||||
|
|
||||||
if book.sync_cover and data.get('covers') and len(data['covers']):
|
if book.sync_cover and data.get('covers') and data['covers']:
|
||||||
book.cover.save(*get_cover(data['covers'][0]), save=True)
|
book.cover.save(*get_cover(data['covers'][0]), save=True)
|
||||||
|
|
||||||
return book
|
return book
|
||||||
|
@ -119,4 +114,3 @@ def get_cover(cover_url):
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
image_content = ContentFile(response.content)
|
image_content = ContentFile(response.content)
|
||||||
return [image_name, image_content]
|
return [image_name, image_content]
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
''' openlibrary data connector '''
|
''' openlibrary data connector '''
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
from django.db import transaction
|
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from fedireads import models
|
from fedireads import models
|
||||||
from .abstract_connector import AbstractConnector, SearchResult
|
from .abstract_connector import AbstractConnector, SearchResult
|
||||||
from .abstract_connector import update_from_mappings, get_date
|
from .abstract_connector import update_from_mappings, get_date
|
||||||
|
@ -247,7 +248,7 @@ def get_languages(language_blob):
|
||||||
|
|
||||||
def pick_default_edition(options):
|
def pick_default_edition(options):
|
||||||
''' favor physical copies with covers in english '''
|
''' favor physical copies with covers in english '''
|
||||||
if not len(options):
|
if not options:
|
||||||
return None
|
return None
|
||||||
if len(options) == 1:
|
if len(options) == 1:
|
||||||
return options[0]
|
return options[0]
|
||||||
|
@ -261,5 +262,3 @@ def pick_default_edition(options):
|
||||||
options = [e for e in options if e.get('isbn_13')] or options
|
options = [e for e in options if e.get('isbn_13')] or options
|
||||||
options = [e for e in options if e.get('ocaid')] or options
|
options = [e for e in options if e.get('ocaid')] or options
|
||||||
return options[0]
|
return options[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,4 +38,3 @@ class Connector(AbstractConnector):
|
||||||
|
|
||||||
def update_book(self, book_obj):
|
def update_book(self, book_obj):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
''' usin django model forms '''
|
''' usin django model forms '''
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.forms import ModelForm, PasswordInput
|
||||||
from django.forms import ModelForm, PasswordInput, IntegerField
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from fedireads import models
|
from fedireads import models
|
||||||
|
@ -117,4 +116,3 @@ class EditionForm(ModelForm):
|
||||||
|
|
||||||
class ImportForm(forms.Form):
|
class ImportForm(forms.Form):
|
||||||
csv_file = forms.FileField()
|
csv_file = forms.FileField()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
''' handles all of the activity coming in to the server '''
|
''' handles all of the activity coming in to the server '''
|
||||||
|
import json
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
from Crypto.Hash import SHA256
|
from Crypto.Hash import SHA256
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
|
@ -7,7 +8,6 @@ import django.db.utils
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
import json
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from fedireads import models, outgoing
|
from fedireads import models, outgoing
|
||||||
|
@ -318,4 +318,3 @@ def handle_tag(activity):
|
||||||
if not user.local:
|
if not user.local:
|
||||||
book = activity['target']['id'].split('/')[-1]
|
book = activity['target']['id'].split('/')[-1]
|
||||||
status_builder.create_tag(user, book, activity['object']['name'])
|
status_builder.create_tag(user, book, activity['object']['name'])
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
''' database schema for books and shelves '''
|
''' database schema for books and shelves '''
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from model_utils.managers import InheritanceManager
|
from model_utils.managers import InheritanceManager
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from fedireads.settings import DOMAIN
|
from fedireads.settings import DOMAIN
|
||||||
from fedireads.utils.fields import JSONField, ArrayField
|
from fedireads.utils.fields import JSONField, ArrayField
|
||||||
|
@ -155,4 +156,3 @@ class Author(FedireadsModel):
|
||||||
models.CharField(max_length=255), blank=True, default=list
|
models.CharField(max_length=255), blank=True, default=list
|
||||||
)
|
)
|
||||||
bio = models.TextField(null=True, blank=True)
|
bio = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
''' models for storing different kinds of Activities '''
|
''' models for storing different kinds of Activities '''
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from model_utils.managers import InheritanceManager
|
from model_utils.managers import InheritanceManager
|
||||||
import urllib.parse
|
|
||||||
|
|
||||||
from fedireads.utils.models import FedireadsModel
|
from fedireads.utils.models import FedireadsModel
|
||||||
|
|
||||||
|
@ -15,7 +16,8 @@ class Status(FedireadsModel):
|
||||||
status_type = models.CharField(max_length=255, default='Note')
|
status_type = models.CharField(max_length=255, default='Note')
|
||||||
content = models.TextField(blank=True, null=True)
|
content = models.TextField(blank=True, null=True)
|
||||||
mention_users = models.ManyToManyField('User', related_name='mention_user')
|
mention_users = models.ManyToManyField('User', related_name='mention_user')
|
||||||
mention_books = models.ManyToManyField('Edition', related_name='mention_book')
|
mention_books = models.ManyToManyField(
|
||||||
|
'Edition', related_name='mention_book')
|
||||||
activity_type = models.CharField(max_length=255, default='Note')
|
activity_type = models.CharField(max_length=255, default='Note')
|
||||||
local = models.BooleanField(default=True)
|
local = models.BooleanField(default=True)
|
||||||
privacy = models.CharField(max_length=255, default='public')
|
privacy = models.CharField(max_length=255, default='public')
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from fedireads.models import Shelf
|
from fedireads.models.shelf import Shelf
|
||||||
from fedireads.settings import DOMAIN
|
from fedireads.settings import DOMAIN
|
||||||
from fedireads.utils.models import FedireadsModel
|
from fedireads.utils.models import FedireadsModel
|
||||||
|
|
||||||
|
@ -190,4 +190,3 @@ def execute_after_save(sender, instance, created, *args, **kwargs):
|
||||||
user=instance,
|
user=instance,
|
||||||
editable=False
|
editable=False
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
|
|
|
@ -37,10 +37,10 @@ def outbox(request, username):
|
||||||
filters = {}
|
filters = {}
|
||||||
# params for the outbox page id
|
# params for the outbox page id
|
||||||
params = {'page': 'true'}
|
params = {'page': 'true'}
|
||||||
if min_id != None:
|
if min_id is not None:
|
||||||
params['min_id'] = min_id
|
params['min_id'] = min_id
|
||||||
filters['id__gt'] = min_id
|
filters['id__gt'] = min_id
|
||||||
if max_id != None:
|
if max_id is not None:
|
||||||
params['max_id'] = max_id
|
params['max_id'] = max_id
|
||||||
filters['id__lte'] = max_id
|
filters['id__lte'] = max_id
|
||||||
|
|
||||||
|
@ -224,7 +224,8 @@ def handle_review(user, book, name, content, rating):
|
||||||
fr_serializer = activitypub.get_review
|
fr_serializer = activitypub.get_review
|
||||||
ap_serializer = activitypub.get_review_article
|
ap_serializer = activitypub.get_review_article
|
||||||
handle_status(
|
handle_status(
|
||||||
user, book, builder, fr_serializer, ap_serializer, name, content, rating)
|
user, book, builder, fr_serializer,
|
||||||
|
ap_serializer, name, content, rating)
|
||||||
|
|
||||||
|
|
||||||
def handle_quotation(user, book, content, quote):
|
def handle_quotation(user, book, content, quote):
|
||||||
|
@ -310,7 +311,8 @@ def handle_favorite(user, status):
|
||||||
return
|
return
|
||||||
|
|
||||||
fav_activity = activitypub.get_favorite(favorite)
|
fav_activity = activitypub.get_favorite(favorite)
|
||||||
broadcast(user, fav_activity, privacy='direct', direct_recipients=[status.user])
|
broadcast(
|
||||||
|
user, fav_activity, privacy='direct', direct_recipients=[status.user])
|
||||||
|
|
||||||
|
|
||||||
def handle_unfavorite(user, status):
|
def handle_unfavorite(user, status):
|
||||||
|
@ -356,4 +358,3 @@ def handle_update_user(user):
|
||||||
actor = activitypub.get_actor(user)
|
actor = activitypub.get_actor(user)
|
||||||
update_activity = activitypub.get_update(user, actor)
|
update_activity = activitypub.get_update(user, actor)
|
||||||
broadcast(user, update_activity)
|
broadcast(user, update_activity)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
''' manage remote users '''
|
''' manage remote users '''
|
||||||
import requests
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
import requests
|
||||||
|
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
|
||||||
|
@ -118,4 +118,3 @@ def get_or_create_remote_server(domain):
|
||||||
application_version=data['software']['version'],
|
application_version=data['software']['version'],
|
||||||
)
|
)
|
||||||
return server
|
return server
|
||||||
|
|
||||||
|
|
|
@ -14,4 +14,3 @@ def sync_book_data():
|
||||||
for book in books:
|
for book in books:
|
||||||
# TODO: create background tasks
|
# TODO: create background tasks
|
||||||
books_manager.update_book(book)
|
books_manager.update_book(book)
|
||||||
|
|
||||||
|
|
|
@ -48,4 +48,3 @@ class InputHtmlParser(HTMLParser):
|
||||||
if not self.allow_html:
|
if not self.allow_html:
|
||||||
return ''.join(v for (k, v) in self.output if k == 'data')
|
return ''.join(v for (k, v) in self.output if k == 'data')
|
||||||
return ''.join(v for (k, v) in self.output)
|
return ''.join(v for (k, v) in self.output)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
''' background tasks '''
|
''' background tasks '''
|
||||||
from celery import Celery
|
|
||||||
import os
|
import os
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
from fedireads import settings
|
from fedireads import settings
|
||||||
|
|
||||||
|
@ -10,5 +10,3 @@ app = Celery(
|
||||||
'tasks',
|
'tasks',
|
||||||
broker=settings.CELERY_BROKER,
|
broker=settings.CELERY_BROKER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ def shelve_button_identifier(context, book):
|
||||||
identifier = shelf.shelf.identifier
|
identifier = shelf.shelf.identifier
|
||||||
if identifier == 'to-read':
|
if identifier == 'to-read':
|
||||||
return 'reading'
|
return 'reading'
|
||||||
elif identifier == 'reading':
|
if identifier == 'reading':
|
||||||
return 'read'
|
return 'read'
|
||||||
return 'to-read'
|
return 'to-read'
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ def shelve_button_text(context, book):
|
||||||
identifier = shelf.shelf.identifier
|
identifier = shelf.shelf.identifier
|
||||||
if identifier == 'to-read':
|
if identifier == 'to-read':
|
||||||
return 'Start reading'
|
return 'Start reading'
|
||||||
elif identifier == 'reading':
|
if identifier == 'reading':
|
||||||
return 'I\'m done!'
|
return 'I\'m done!'
|
||||||
return 'Want to read'
|
return 'Want to read'
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ if 'sqlite' in settings.DATABASES['default']['ENGINE']:
|
||||||
"""Care for DjangoArrayField's kwargs."""
|
"""Care for DjangoArrayField's kwargs."""
|
||||||
self.base_field = base_field
|
self.base_field = base_field
|
||||||
self.size = size
|
self.size = size
|
||||||
return super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def deconstruct(self):
|
def deconstruct(self):
|
||||||
"""Need to create migrations properly."""
|
"""Need to create migrations properly."""
|
||||||
|
@ -64,4 +64,4 @@ if 'sqlite' in settings.DATABASES['default']['ENGINE']:
|
||||||
'base_field': self.base_field.clone(),
|
'base_field': self.base_field.clone(),
|
||||||
'size': self.size,
|
'size': self.size,
|
||||||
})
|
})
|
||||||
return name, path, args, kwargs
|
return name, path, args, kwargs
|
||||||
|
|
|
@ -20,4 +20,3 @@ class FedireadsModel(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
|
@ -453,4 +453,3 @@ def import_data(request):
|
||||||
'failures': failures,
|
'failures': failures,
|
||||||
})
|
})
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|
|
@ -207,18 +207,18 @@ def user_page(request, username, subpage=None):
|
||||||
if subpage == 'followers':
|
if subpage == 'followers':
|
||||||
data['followers'] = user.followers.all()
|
data['followers'] = user.followers.all()
|
||||||
return TemplateResponse(request, 'followers.html', data)
|
return TemplateResponse(request, 'followers.html', data)
|
||||||
elif subpage == 'following':
|
if subpage == 'following':
|
||||||
data['following'] = user.following.all()
|
data['following'] = user.following.all()
|
||||||
return TemplateResponse(request, 'following.html', data)
|
return TemplateResponse(request, 'following.html', data)
|
||||||
elif subpage == 'shelves':
|
if subpage == 'shelves':
|
||||||
data['shelves'] = user.shelf_set.all()
|
data['shelves'] = user.shelf_set.all()
|
||||||
return TemplateResponse(request, 'user_shelves.html', data)
|
return TemplateResponse(request, 'user_shelves.html', data)
|
||||||
else:
|
|
||||||
shelves = get_user_shelf_preview(user)
|
shelves = get_user_shelf_preview(user)
|
||||||
data['shelves'] = shelves
|
data['shelves'] = shelves
|
||||||
activities = get_activity_feed(user, 'self')[:15]
|
activities = get_activity_feed(user, 'self')[:15]
|
||||||
data['activities'] = activities
|
data['activities'] = activities
|
||||||
return TemplateResponse(request, 'user.html', data)
|
return TemplateResponse(request, 'user.html', data)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
|
@ -531,4 +531,3 @@ def get_user_shelf_preview(user, shelf_proportions=None):
|
||||||
'size': shelf.books.count(),
|
'size': shelf.books.count(),
|
||||||
})
|
})
|
||||||
return shelves
|
return shelves
|
||||||
|
|
||||||
|
|
|
@ -107,4 +107,3 @@ def peers(request):
|
||||||
|
|
||||||
names = models.FederatedServer.objects.values_list('server_name', flat=True)
|
names = models.FederatedServer.objects.values_list('server_name', flat=True)
|
||||||
return JsonResponse(list(names), safe=False)
|
return JsonResponse(list(names), safe=False)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue