Skip to content

Commit

Permalink
Merge branch 'main' into production
Browse files Browse the repository at this point in the history
  • Loading branch information
mouse-reeve committed Jan 2, 2024
2 parents 1093e95 + 0756c5a commit 439b0bc
Show file tree
Hide file tree
Showing 328 changed files with 22,807 additions and 5,838 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/django-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Check migrations up-to-date
run: |
python ./manage.py makemigrations --check
env:
SECRET_KEY: beepbeep
DOMAIN: your.domain.here
EMAIL_HOST: ""
EMAIL_HOST_USER: ""
EMAIL_HOST_PASSWORD: ""
- name: Run Tests
env:
SECRET_KEY: beepbeep
Expand Down
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'trailingComma': 'es5'
5 changes: 3 additions & 2 deletions FEDERATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ User relationship interactions follow the standard ActivityPub spec.
- `Block`: prevent users from seeing one another's statuses, and prevents the blocked user from viewing the actor's profile
- `Update`: updates a user's profile and settings
- `Delete`: deactivates a user
- `Undo`: reverses a `Follow` or `Block`
- `Undo`: reverses a `Block` or `Follow`

### Activities
- `Create/Status`: saves a new status in the database.
- `Delete/Status`: Removes a status
- `Like/Status`: Creates a favorite on the status
- `Announce/Status`: Boosts the status into the actor's timeline
- `Undo/*`,: Reverses a `Like` or `Announce`
- `Undo/*`,: Reverses an `Announce`, `Like`, or `Move`
- `Move/User`: Moves a user from one ActivityPub id to another.

### Collections
User's books and lists are represented by [`OrderedCollection`](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection)
Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.7.0
1 change: 1 addition & 0 deletions bookwyrm/activitypub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .verbs import Follow, Accept, Reject, Block
from .verbs import Add, Remove
from .verbs import Announce, Like
from .verbs import Move

# this creates a list of all the Activity types that we can serialize,
# so when an Activity comes in from outside, we can check if it's known
Expand Down
20 changes: 8 additions & 12 deletions bookwyrm/activitypub/base_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def serialize(self, **kwargs):
omit = kwargs.get("omit", ())
data = self.__dict__.copy()
# recursively serialize
for (k, v) in data.items():
for k, v in data.items():
try:
if issubclass(type(v), ActivityObject):
data[k] = v.serialize()
Expand Down Expand Up @@ -396,19 +396,15 @@ def resolve_remote_id(

def get_representative():
"""Get or create an actor representing the instance
to sign requests to 'secure mastodon' servers"""
username = f"{INSTANCE_ACTOR_USERNAME}@{DOMAIN}"
email = "bookwyrm@localhost"
try:
user = models.User.objects.get(username=username)
except models.User.DoesNotExist:
user = models.User.objects.create_user(
username=username,
email=email,
to sign outgoing HTTP GET requests"""
return models.User.objects.get_or_create(
username=f"{INSTANCE_ACTOR_USERNAME}@{DOMAIN}",
defaults=dict(
email="bookwyrm@localhost",
local=True,
localname=INSTANCE_ACTOR_USERNAME,
)
return user
),
)[0]


def get_activitypub_data(url):
Expand Down
4 changes: 2 additions & 2 deletions bookwyrm/activitypub/book.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ class BookData(ActivityObject):
aasin: Optional[str] = None
isfdb: Optional[str] = None
lastEditedBy: Optional[str] = None
links: list[str] = field(default_factory=list)
fileLinks: list[str] = field(default_factory=list)


# pylint: disable=invalid-name
Expand All @@ -45,6 +43,8 @@ class Book(BookData):
firstPublishedDate: str = ""
publishedDate: str = ""

fileLinks: list[str] = field(default_factory=list)

cover: Optional[Document] = None
type: str = "Book"

Expand Down
2 changes: 2 additions & 0 deletions bookwyrm/activitypub/person.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ class Person(ActivityObject):
manuallyApprovesFollowers: str = False
discoverable: str = False
hideFollows: str = False
movedTo: str = None
alsoKnownAs: dict[str] = None
type: str = "Person"
43 changes: 40 additions & 3 deletions bookwyrm/activitypub/verbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,19 @@ class Reject(Verb):
type: str = "Reject"

def action(self, allow_external_connections=True):
"""reject a follow request"""
obj = self.object.to_model(save=False, allow_create=False)
obj.reject()
"""reject a follow or follow request"""

for model_name in ["UserFollowRequest", "UserFollows", None]:
model = apps.get_model(f"bookwyrm.{model_name}") if model_name else None
if obj := self.object.to_model(
model=model,
save=False,
allow_create=False,
allow_external_connections=allow_external_connections,
):
# Reject the first model that can be built.
obj.reject()
break


@dataclass(init=False)
Expand Down Expand Up @@ -231,3 +241,30 @@ class Announce(Verb):
def action(self, allow_external_connections=True):
"""boost"""
self.to_model(allow_external_connections=allow_external_connections)


@dataclass(init=False)
class Move(Verb):
"""a user moving an object"""

object: str
type: str = "Move"
origin: str = None
target: str = None

def action(self, allow_external_connections=True):
"""move"""

object_is_user = resolve_remote_id(remote_id=self.object, model="User")

if object_is_user:
model = apps.get_model("bookwyrm.MoveUser")

self.to_model(
model=model,
save=True,
allow_external_connections=allow_external_connections,
)
else:
# we might do something with this to move other objects at some point
pass
2 changes: 1 addition & 1 deletion bookwyrm/book_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def search_title_author(

# filter out multiple editions of the same work
list_results = []
for work_id in set(editions_of_work[:30]):
for work_id in editions_of_work[:30]:
result = (
results.filter(parent_work=work_id)
.order_by("-rank", "-edition_rank")
Expand Down
7 changes: 3 additions & 4 deletions bookwyrm/forms/books.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
""" using django model forms """
from django import forms

from file_resubmit.widgets import ResubmitImageWidget

from bookwyrm import models
from bookwyrm.models.fields import ClearableFileInputWithWarning
from .custom_form import CustomForm
from .widgets import ArrayWidget, SelectDateWidget, Select

Expand Down Expand Up @@ -70,9 +71,7 @@ class Meta:
"published_date": SelectDateWidget(
attrs={"aria-describedby": "desc_published_date"}
),
"cover": ClearableFileInputWithWarning(
attrs={"aria-describedby": "desc_cover"}
),
"cover": ResubmitImageWidget(attrs={"aria-describedby": "desc_cover"}),
"physical_format": Select(
attrs={"aria-describedby": "desc_physical_format"}
),
Expand Down
16 changes: 16 additions & 0 deletions bookwyrm/forms/edit_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,22 @@ class Meta:
fields = ["password"]


class MoveUserForm(CustomForm):
target = forms.CharField(widget=forms.TextInput)

class Meta:
model = models.User
fields = ["password"]


class AliasUserForm(CustomForm):
username = forms.CharField(widget=forms.TextInput)

class Meta:
model = models.User
fields = ["password"]


class ChangePasswordForm(CustomForm):
current_password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
Expand Down
4 changes: 4 additions & 0 deletions bookwyrm/forms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class ImportForm(forms.Form):
csv_file = forms.FileField()


class ImportUserForm(forms.Form):
archive_file = forms.FileField()


class ShelfForm(CustomForm):
class Meta:
model = models.Shelf
Expand Down
1 change: 1 addition & 0 deletions bookwyrm/importers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" import classes """

from .importer import Importer
from .bookwyrm_import import BookwyrmImporter
from .calibre_import import CalibreImporter
from .goodreads_import import GoodreadsImporter
from .librarything_import import LibrarythingImporter
Expand Down
24 changes: 24 additions & 0 deletions bookwyrm/importers/bookwyrm_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Import data from Bookwyrm export files"""
from django.http import QueryDict

from bookwyrm.models import User
from bookwyrm.models.bookwyrm_import_job import BookwyrmImportJob


class BookwyrmImporter:
"""Import a Bookwyrm User export file.
This is kind of a combination of an importer and a connector.
"""

# pylint: disable=no-self-use
def process_import(
self, user: User, archive_file: bytes, settings: QueryDict
) -> BookwyrmImportJob:
"""import user data from a Bookwyrm export file"""

required = [k for k in settings if settings.get(k) == "on"]

job = BookwyrmImportJob.objects.create(
user=user, archive_file=archive_file, required=required
)
return job
7 changes: 6 additions & 1 deletion bookwyrm/isbn/isbn.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ def hyphenate(self, isbn_13: Optional[str]) -> Optional[str]:
self.__element_tree = ElementTree.parse(self.__range_file_path)

gs1_prefix = isbn_13[:3]
reg_group = self.__find_reg_group(isbn_13, gs1_prefix)
try:
reg_group = self.__find_reg_group(isbn_13, gs1_prefix)
except ValueError:
# if the reg groups are invalid, just return the original isbn
return isbn_13

if reg_group is None:
return isbn_13 # failed to hyphenate

Expand Down
1 change: 1 addition & 0 deletions bookwyrm/middleware/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
""" look at all this nice middleware! """
from .timezone_middleware import TimezoneMiddleware
from .ip_middleware import IPBlocklistMiddleware
from .file_too_big import FileTooBig
30 changes: 30 additions & 0 deletions bookwyrm/middleware/file_too_big.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Middleware to display a custom 413 error page"""

from django.http import HttpResponse
from django.shortcuts import render
from django.core.exceptions import RequestDataTooBig


class FileTooBig:
"""Middleware to display a custom page when a
RequestDataTooBig exception is thrown"""

def __init__(self, get_response):
"""boilerplate __init__ from Django docs"""

self.get_response = get_response

def __call__(self, request):
"""If RequestDataTooBig is thrown, render the 413 error page"""

try:
body = request.body # pylint: disable=unused-variable

except RequestDataTooBig:

rendered = render(request, "413.html")
response = HttpResponse(rendered)
return response

response = self.get_response(request)
return response
4 changes: 3 additions & 1 deletion bookwyrm/migrations/0179_populate_sort_title.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@ class Migration(migrations.Migration):
]

operations = [
migrations.RunPython(populate_sort_title),
migrations.RunPython(
populate_sort_title, reverse_code=migrations.RunPython.noop
),
]
Loading

0 comments on commit 439b0bc

Please sign in to comment.