mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-06-13 18:59:24 +00:00
Merge branch 'main' into header-links
This commit is contained in:
commit
336c62bfc2
71
README.md
71
README.md
|
@ -1,60 +1,45 @@
|
||||||
# BookWyrm
|
# BookWyrm
|
||||||
|
|
||||||
Social reading and reviewing, decentralized with ActivityPub
|
[![](https://img.shields.io/github/release/bookwyrm-social/bookwyrm.svg?colorB=58839b)](https://github.com/bookwyrm-social/bookwyrm/releases)
|
||||||
|
[![Run Python Tests](https://github.com/bookwyrm-social/bookwyrm/actions/workflows/django-tests.yml/badge.svg)](https://github.com/bookwyrm-social/bookwyrm/actions/workflows/django-tests.yml)
|
||||||
|
[![Pylint](https://github.com/bookwyrm-social/bookwyrm/actions/workflows/pylint.yml/badge.svg)](https://github.com/bookwyrm-social/bookwyrm/actions/workflows/pylint.yml)
|
||||||
|
|
||||||
## Contents
|
BookWyrm is a social network for tracking your reading, talking about books, writing reviews, and discovering what to read next. Federation allows BookWyrm users to join small, trusted communities that can connect with one another, and with other ActivityPub services like [Mastodon](https://joinmastodon.org/) and [Pleroma](http://pleroma.social/).
|
||||||
- [Joining BookWyrm](#joining-bookwyrm)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [About BookWyrm](#about-bookwyrm)
|
|
||||||
- [What it is and isn't](#what-it-is-and-isnt)
|
|
||||||
- [The role of federation](#the-role-of-federation)
|
|
||||||
- [Features](#features)
|
|
||||||
- [Set up BookWyrm](#set-up-bookwyrm)
|
|
||||||
|
|
||||||
## Joining BookWyrm
|
|
||||||
If you'd like to join an instance, you can check out the [instances](https://joinbookwyrm.com/instances/) list.
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Links
|
||||||
See [contributing](https://docs.joinbookwyrm.com/contributing.html) for code, translation or monetary contributions.
|
|
||||||
|
[![Mastodon Follow](https://img.shields.io/mastodon/follow/000146121?domain=https%3A%2F%2Ftech.lgbt&style=social)](https://tech.lgbt/@bookwyrm)
|
||||||
|
[![Twitter Follow](https://img.shields.io/twitter/follow/BookWyrmSocial?style=social)](https://twitter.com/BookWyrmSocial)
|
||||||
|
|
||||||
|
- [Project homepage](https://joinbookwyrm.com/)
|
||||||
|
- [Support](https://patreon.com/bookwyrm)
|
||||||
|
- [Documentation](https://docs.joinbookwyrm.com/)
|
||||||
|
|
||||||
|
|
||||||
## About BookWyrm
|
## About BookWyrm
|
||||||
### What it is and isn't
|
|
||||||
BookWyrm is a platform for social reading. You can use it to track what you're reading, review books, and follow your friends. It isn't primarily meant for cataloguing or as a data-source for books, but it does do both of those things to some degree.
|
BookWyrm is a platform for social reading. You can use it to track what you're reading, review books, and follow your friends. It isn't primarily meant for cataloguing or as a data-source for books, but it does do both of those things to some degree.
|
||||||
|
|
||||||
### The role of federation
|
## Federation
|
||||||
BookWyrm is built on [ActivityPub](http://activitypub.rocks/). With ActivityPub, it inter-operates with different instances of BookWyrm, and other ActivityPub compliant services, like Mastodon. This means you can run an instance for your book club, and still follow your friend who posts on a server devoted to 20th century Russian speculative fiction. It also means that your friend on mastodon can read and comment on a book review that you post on your BookWyrm instance.
|
BookWyrm is built on [ActivityPub](http://activitypub.rocks/). With ActivityPub, it inter-operates with different instances of BookWyrm, and other ActivityPub compliant services, like Mastodon. This means you can run an instance for your book club, and still follow your friend who posts on a server devoted to 20th century Russian speculative fiction. It also means that your friend on mastodon can read and comment on a book review that you post on your BookWyrm instance.
|
||||||
|
|
||||||
Federation makes it possible to have small, self-determining communities, in contrast to the monolithic service you find on GoodReads or Twitter. An instance can be focused on a particular interest, be just for a group of friends, or anything else that brings people together. Each community can choose which other instances they want to federate with, and moderate and run their community autonomously. Check out https://runyourown.social/ to get a sense of the philosophy and logistics behind small, high-trust social networks.
|
Federation makes it possible to have small, self-determining communities, in contrast to the monolithic service you find on GoodReads or Twitter. An instance can be focused on a particular interest, be just for a group of friends, or anything else that brings people together. Each community can choose which other instances they want to federate with, and moderate and run their community autonomously. Check out https://runyourown.social/ to get a sense of the philosophy and logistics behind small, high-trust social networks.
|
||||||
|
|
||||||
### Features
|
## Features
|
||||||
Since the project is still in its early stages, the features are growing every day, and there is plenty of room for suggestions and ideas. Open an [issue](https://github.com/bookwyrm-social/bookwyrm/issues) to get the conversation going!
|
|
||||||
- Posting about books
|
|
||||||
- Compose reviews, with or without ratings, which are aggregated in the book page
|
|
||||||
- Compose other kinds of statuses about books, such as:
|
|
||||||
- Comments on a book
|
|
||||||
- Quotes or excerpts
|
|
||||||
- Reply to statuses
|
|
||||||
- View aggregate reviews of a book across connected BookWyrm instances
|
|
||||||
- Differentiate local and federated reviews and rating in your activity feed
|
|
||||||
- Track reading activity
|
|
||||||
- Shelve books on default "to-read," "currently reading," and "read" shelves
|
|
||||||
- Create custom shelves
|
|
||||||
- Store started reading/finished reading dates, as well as progress updates along the way
|
|
||||||
- Update followers about reading activity (optionally, and with granular privacy controls)
|
|
||||||
- Create lists of books which can be open to submissions from anyone, curated, or only edited by the creator
|
|
||||||
- Federation with ActivityPub
|
|
||||||
- Broadcast and receive user statuses and activity
|
|
||||||
- Share book data between instances to create a networked database of metadata
|
|
||||||
- Identify shared books across instances and aggregate related content
|
|
||||||
- Follow and interact with users across BookWyrm instances
|
|
||||||
- Inter-operate with non-BookWyrm ActivityPub services (currently, Mastodon is supported)
|
|
||||||
- Granular privacy controls
|
|
||||||
- Private, followers-only, and public privacy levels for posting, shelves, and lists
|
|
||||||
- Option for users to manually approve followers
|
|
||||||
- Allow blocking and flagging for moderation
|
|
||||||
|
|
||||||
### The Tech Stack
|
### Post about books
|
||||||
|
Compose reviews, comment on what you're reading, and post quotes from books. You can converse with other BookWyrm users across the network about what they're reading.
|
||||||
|
|
||||||
|
### Track reading activity
|
||||||
|
Keep track of what books you've read, and what books you'd like to read in the future.
|
||||||
|
|
||||||
|
### Federation with ActivityPub
|
||||||
|
Federation allows you to interact with users on other instances and services, and also shares metadata about books and authors, which collaboratively builds a decentralized database of books.
|
||||||
|
|
||||||
|
### Privacy and moderation
|
||||||
|
Users and administrators can control who can see thier posts and what other instances to federate with.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
Web backend
|
Web backend
|
||||||
- [Django](https://www.djangoproject.com/) web server
|
- [Django](https://www.djangoproject.com/) web server
|
||||||
- [PostgreSQL](https://www.postgresql.org/) database
|
- [PostgreSQL](https://www.postgresql.org/) database
|
||||||
|
|
|
@ -53,7 +53,7 @@ async def get_results(session, url, min_confidence, query, connector):
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
logger.info("Connection timed out for url: %s", url)
|
logger.info("Connection timed out for url: %s", url)
|
||||||
except aiohttp.ClientError as err:
|
except aiohttp.ClientError as err:
|
||||||
logger.exception(err)
|
logger.info(err)
|
||||||
|
|
||||||
|
|
||||||
async def async_connector_search(query, items, min_confidence):
|
async def async_connector_search(query, items, min_confidence):
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
""" using django model forms """
|
""" using django model forms """
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib.auth.password_validation import validate_password
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
from bookwyrm.models.fields import ClearableFileInputWithWarning
|
from bookwyrm.models.fields import ClearableFileInputWithWarning
|
||||||
|
@ -66,3 +69,33 @@ class DeleteUserForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.User
|
model = models.User
|
||||||
fields = ["password"]
|
fields = ["password"]
|
||||||
|
|
||||||
|
|
||||||
|
class ChangePasswordForm(CustomForm):
|
||||||
|
current_password = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
confirm_password = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.User
|
||||||
|
fields = ["password"]
|
||||||
|
widgets = {
|
||||||
|
"password": forms.PasswordInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""Make sure passwords match and are valid"""
|
||||||
|
current_password = self.data.get("current_password")
|
||||||
|
if not self.instance.check_password(current_password):
|
||||||
|
self.add_error("current_password", _("Incorrect password"))
|
||||||
|
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
new_password = cleaned_data.get("password")
|
||||||
|
confirm_password = self.data.get("confirm_password")
|
||||||
|
|
||||||
|
if new_password != confirm_password:
|
||||||
|
self.add_error("confirm_password", _("Password does not match"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate_password(new_password)
|
||||||
|
except ValidationError as err:
|
||||||
|
self.add_error("password", err)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
""" Forms for the landing pages """
|
""" Forms for the landing pages """
|
||||||
from django.forms import PasswordInput
|
from django import forms
|
||||||
|
from django.contrib.auth.password_validation import validate_password
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
@ -13,7 +15,7 @@ class LoginForm(CustomForm):
|
||||||
fields = ["localname", "password"]
|
fields = ["localname", "password"]
|
||||||
help_texts = {f: None for f in fields}
|
help_texts = {f: None for f in fields}
|
||||||
widgets = {
|
widgets = {
|
||||||
"password": PasswordInput(),
|
"password": forms.PasswordInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,12 +24,16 @@ class RegisterForm(CustomForm):
|
||||||
model = models.User
|
model = models.User
|
||||||
fields = ["localname", "email", "password"]
|
fields = ["localname", "email", "password"]
|
||||||
help_texts = {f: None for f in fields}
|
help_texts = {f: None for f in fields}
|
||||||
widgets = {"password": PasswordInput()}
|
widgets = {"password": forms.PasswordInput()}
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""Check if the username is taken"""
|
"""Check if the username is taken"""
|
||||||
cleaned_data = super().clean()
|
cleaned_data = super().clean()
|
||||||
localname = cleaned_data.get("localname").strip()
|
localname = cleaned_data.get("localname").strip()
|
||||||
|
try:
|
||||||
|
validate_password(cleaned_data.get("password"))
|
||||||
|
except ValidationError as err:
|
||||||
|
self.add_error("password", err)
|
||||||
if models.User.objects.filter(localname=localname).first():
|
if models.User.objects.filter(localname=localname).first():
|
||||||
self.add_error("localname", _("User with this username already exists"))
|
self.add_error("localname", _("User with this username already exists"))
|
||||||
|
|
||||||
|
@ -43,3 +49,28 @@ class InviteRequestForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.InviteRequest
|
model = models.InviteRequest
|
||||||
fields = ["email", "answer"]
|
fields = ["email", "answer"]
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetForm(CustomForm):
|
||||||
|
confirm_password = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.User
|
||||||
|
fields = ["password"]
|
||||||
|
widgets = {
|
||||||
|
"password": forms.PasswordInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""Make sure the passwords match and are valid"""
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
new_password = cleaned_data.get("password")
|
||||||
|
confirm_password = self.data.get("confirm_password")
|
||||||
|
|
||||||
|
if new_password != confirm_password:
|
||||||
|
self.add_error("confirm_password", _("Password does not match"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate_password(new_password)
|
||||||
|
except ValidationError as err:
|
||||||
|
self.add_error("password", err)
|
||||||
|
|
40
bookwyrm/migrations/0154_alter_user_preferred_language.py
Normal file
40
bookwyrm/migrations/0154_alter_user_preferred_language.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# Generated by Django 3.2.14 on 2022-07-15 19:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0153_merge_20220706_2141"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="user",
|
||||||
|
name="preferred_language",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
choices=[
|
||||||
|
("en-us", "English"),
|
||||||
|
("ca-es", "Català (Catalan)"),
|
||||||
|
("de-de", "Deutsch (German)"),
|
||||||
|
("es-es", "Español (Spanish)"),
|
||||||
|
("gl-es", "Galego (Galician)"),
|
||||||
|
("it-it", "Italiano (Italian)"),
|
||||||
|
("fi-fi", "Suomi (Finnish)"),
|
||||||
|
("fr-fr", "Français (French)"),
|
||||||
|
("lt-lt", "Lietuvių (Lithuanian)"),
|
||||||
|
("no-no", "Norsk (Norwegian)"),
|
||||||
|
("pt-br", "Português do Brasil (Brazilian Portuguese)"),
|
||||||
|
("pt-pt", "Português Europeu (European Portuguese)"),
|
||||||
|
("ro-ro", "Română (Romanian)"),
|
||||||
|
("sv-se", "Svenska (Swedish)"),
|
||||||
|
("zh-hans", "简体中文 (Simplified Chinese)"),
|
||||||
|
("zh-hant", "繁體中文 (Traditional Chinese)"),
|
||||||
|
],
|
||||||
|
max_length=255,
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
25
bookwyrm/migrations/0155_user_show_guided_tour.py
Normal file
25
bookwyrm/migrations/0155_user_show_guided_tour.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 3.2.14 on 2022-07-09 23:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def existing_users_default(apps, schema_editor):
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
user_model = apps.get_model("bookwyrm", "User")
|
||||||
|
user_model.objects.using(db_alias).filter(local=True).update(show_guided_tour=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0154_alter_user_preferred_language"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="user",
|
||||||
|
name="show_guided_tour",
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.RunPython(existing_users_default, migrations.RunPython.noop),
|
||||||
|
]
|
|
@ -71,7 +71,9 @@ class Notification(BookWyrmModel):
|
||||||
"""Create a notification"""
|
"""Create a notification"""
|
||||||
if related_user and (not user.local or user == related_user):
|
if related_user and (not user.local or user == related_user):
|
||||||
return
|
return
|
||||||
notification, _ = cls.objects.get_or_create(user=user, **kwargs)
|
notification = cls.objects.filter(user=user, **kwargs).first()
|
||||||
|
if not notification:
|
||||||
|
notification = cls.objects.create(user=user, **kwargs)
|
||||||
if related_user:
|
if related_user:
|
||||||
notification.related_users.add(related_user)
|
notification.related_users.add(related_user)
|
||||||
notification.read = False
|
notification.read = False
|
||||||
|
@ -298,8 +300,10 @@ def notify_user_on_follow(sender, instance, created, *args, **kwargs):
|
||||||
notification.read = False
|
notification.read = False
|
||||||
notification.save()
|
notification.save()
|
||||||
else:
|
else:
|
||||||
|
# Only group unread follows
|
||||||
Notification.notify(
|
Notification.notify(
|
||||||
instance.user_object,
|
instance.user_object,
|
||||||
instance.user_subject,
|
instance.user_subject,
|
||||||
notification_type=Notification.FOLLOW,
|
notification_type=Notification.FOLLOW,
|
||||||
|
read=False,
|
||||||
)
|
)
|
||||||
|
|
|
@ -218,7 +218,8 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
"""certain types of status aren't editable"""
|
"""certain types of status aren't editable"""
|
||||||
# first, the standard raise
|
# first, the standard raise
|
||||||
super().raise_not_editable(viewer)
|
super().raise_not_editable(viewer)
|
||||||
if isinstance(self, (GeneratedNote, ReviewRating)):
|
# if it's an edit (not a create) you can only edit content statuses
|
||||||
|
if self.id and isinstance(self, (GeneratedNote, ReviewRating)):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -143,6 +143,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
show_goal = models.BooleanField(default=True)
|
show_goal = models.BooleanField(default=True)
|
||||||
show_suggested_users = models.BooleanField(default=True)
|
show_suggested_users = models.BooleanField(default=True)
|
||||||
discoverable = fields.BooleanField(default=False)
|
discoverable = fields.BooleanField(default=False)
|
||||||
|
show_guided_tour = models.BooleanField(default=True)
|
||||||
|
|
||||||
# feed options
|
# feed options
|
||||||
feed_status_types = ArrayField(
|
feed_status_types = ArrayField(
|
||||||
|
@ -174,6 +175,11 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
property_fields = [("following_link", "following")]
|
property_fields = [("following_link", "following")]
|
||||||
field_tracker = FieldTracker(fields=["name", "avatar"])
|
field_tracker = FieldTracker(fields=["name", "avatar"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_follower_requests(self):
|
||||||
|
"""Follow requests from active users"""
|
||||||
|
return self.follower_requests.filter(is_active=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def confirmation_link(self):
|
def confirmation_link(self):
|
||||||
"""helper for generating confirmation links"""
|
"""helper for generating confirmation links"""
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
env = Env()
|
env = Env()
|
||||||
env.read_env()
|
env.read_env()
|
||||||
DOMAIN = env("DOMAIN")
|
DOMAIN = env("DOMAIN")
|
||||||
VERSION = "0.4.2"
|
VERSION = "0.4.4"
|
||||||
|
|
||||||
RELEASE_API = env(
|
RELEASE_API = env(
|
||||||
"RELEASE_API",
|
"RELEASE_API",
|
||||||
|
@ -280,6 +280,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
LANGUAGE_CODE = env("LANGUAGE_CODE", "en-us")
|
LANGUAGE_CODE = env("LANGUAGE_CODE", "en-us")
|
||||||
LANGUAGES = [
|
LANGUAGES = [
|
||||||
("en-us", _("English")),
|
("en-us", _("English")),
|
||||||
|
("ca-es", _("Català (Catalan)")),
|
||||||
("de-de", _("Deutsch (German)")),
|
("de-de", _("Deutsch (German)")),
|
||||||
("es-es", _("Español (Spanish)")),
|
("es-es", _("Español (Spanish)")),
|
||||||
("gl-es", _("Galego (Galician)")),
|
("gl-es", _("Galego (Galician)")),
|
||||||
|
|
|
@ -6,11 +6,11 @@ ol.ordered-list {
|
||||||
counter-reset: list-counter;
|
counter-reset: list-counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol.ordered-list li {
|
ol.ordered-list > li {
|
||||||
counter-increment: list-counter;
|
counter-increment: list-counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol.ordered-list li::before {
|
ol.ordered-list > li::before {
|
||||||
content: counter(list-counter);
|
content: counter(list-counter);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -20px;
|
left: -20px;
|
||||||
|
|
|
@ -94,3 +94,4 @@ $family-secondary: $family-sans-serif;
|
||||||
|
|
||||||
@import "../bookwyrm.scss";
|
@import "../bookwyrm.scss";
|
||||||
@import "../vendor/icons.css";
|
@import "../vendor/icons.css";
|
||||||
|
@import "../vendor/shepherd.scss";
|
||||||
|
|
|
@ -67,3 +67,4 @@ $family-secondary: $family-sans-serif;
|
||||||
|
|
||||||
@import "../bookwyrm.scss";
|
@import "../bookwyrm.scss";
|
||||||
@import "../vendor/icons.css";
|
@import "../vendor/icons.css";
|
||||||
|
@import "../vendor/shepherd.scss";
|
||||||
|
|
48
bookwyrm/static/css/vendor/shepherd.scss
vendored
Normal file
48
bookwyrm/static/css/vendor/shepherd.scss
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
Shepherd styles for guided tour.
|
||||||
|
Based on Shepherd v 10.0.0 styles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@use 'bulma/bulma.sass';
|
||||||
|
|
||||||
|
.shepherd-button {
|
||||||
|
@extend .button.mr-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-button.shepherd-button-secondary {
|
||||||
|
@extend .button.is-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-footer {
|
||||||
|
@extend .message-body;
|
||||||
|
@extend .is-info.is-light;
|
||||||
|
border-color: $info-light;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-cancel-icon{background:transparent;border:none;color:hsla(0,0%,50%,.75);cursor:pointer;font-size:2em;font-weight:400;margin:0;padding:0;transition:color .5s ease}.shepherd-cancel-icon:hover{color:rgba(0,0,0,.75)}.shepherd-has-title .shepherd-content .shepherd-cancel-icon{color:hsla(0,0%,50%,.75)}.shepherd-has-title .shepherd-content .shepherd-cancel-icon:hover{color:rgba(0,0,0,.75)}
|
||||||
|
|
||||||
|
.shepherd-header {
|
||||||
|
@extend .message-header;
|
||||||
|
@extend .is-info;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-text {
|
||||||
|
@extend .message-body;
|
||||||
|
@extend .is-info.is-light;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-content {
|
||||||
|
@extend .message;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-element{background:$info-light;border-radius:5px;box-shadow:4px 4px 6px rgba(0,0,0,.2);max-width:400px;opacity:0;outline:none;transition:opacity .3s,visibility .3s;visibility:hidden;width:100%;z-index:9999}.shepherd-enabled.shepherd-element{opacity:1;visibility:visible}.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered){opacity:0;pointer-events:none;visibility:hidden}.shepherd-element,.shepherd-element *,.shepherd-element :after,.shepherd-element :before{box-sizing:border-box}.shepherd-arrow,.shepherd-arrow:before{height:16px;position:absolute;width:16px;z-index:-1}.shepherd-arrow:before{background:$info-light;box-shadow:0 2px 4px rgba(0,0,0,.2);content:"";transform:rotate(45deg)}.shepherd-element[data-popper-placement^=top]>.shepherd-arrow{bottom:-8px}.shepherd-element[data-popper-placement^=bottom]>.shepherd-arrow{top:-8px}.shepherd-element[data-popper-placement^=left]>.shepherd-arrow{right:-8px}.shepherd-element[data-popper-placement^=right]>.shepherd-arrow{left:-8px}.shepherd-element.shepherd-centered>.shepherd-arrow{opacity:0}.shepherd-element.shepherd-has-title[data-popper-placement^=bottom]>.shepherd-arrow:before{background-color:$info}.shepherd-target-click-disabled.shepherd-enabled.shepherd-target,.shepherd-target-click-disabled.shepherd-enabled.shepherd-target *{pointer-events:none}
|
||||||
|
|
||||||
|
.shepherd-modal-overlay-container{height:0;left:0;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:0;transition:all .3s ease-out,height 0ms .3s,opacity .3s 0ms;width:100vw;z-index:9997}.shepherd-modal-overlay-container.shepherd-modal-is-visible{height:100vh;opacity:.5;transform:translateZ(0);transition:all .3s ease-out,height 0s 0s,opacity .3s 0s}.shepherd-modal-overlay-container.shepherd-modal-is-visible path{pointer-events:all}
|
||||||
|
|
||||||
|
.tour-element-highlight {
|
||||||
|
border: 5px solid $info;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow:4px 4px 6px rgba(0,0,0,.2);
|
||||||
|
}
|
18
bookwyrm/static/js/guided_tour.js
Normal file
18
bookwyrm/static/js/guided_tour.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Set guided tour user value to False
|
||||||
|
* @param {csrf_token} string
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
function disableGuidedTour(csrf_token) {
|
||||||
|
"use strict";
|
||||||
|
fetch("/guided-tour/False", {
|
||||||
|
headers: {
|
||||||
|
"X-CSRFToken": csrf_token,
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
redirect: "follow",
|
||||||
|
mode: "same-origin",
|
||||||
|
});
|
||||||
|
}
|
120
bookwyrm/static/js/vendor/shepherd.min.js
vendored
Normal file
120
bookwyrm/static/js/vendor/shepherd.min.js
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*! shepherd.js 10.0.0 */
|
||||||
|
|
||||||
|
'use strict';(function(O,pa){"object"===typeof exports&&"undefined"!==typeof module?module.exports=pa():"function"===typeof define&&define.amd?define(pa):(O="undefined"!==typeof globalThis?globalThis:O||self,O.Shepherd=pa())})(this,function(){function O(a,b){return!1!==b.clone&&b.isMergeableObject(a)?ea(Array.isArray(a)?[]:{},a,b):a}function pa(a,b,c){return a.concat(b).map(function(d){return O(d,c)})}function Cb(a){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(a).filter(function(b){return a.propertyIsEnumerable(b)}):
|
||||||
|
[]}function Sa(a){return Object.keys(a).concat(Cb(a))}function Ta(a,b){try{return b in a}catch(c){return!1}}function Db(a,b,c){var d={};c.isMergeableObject(a)&&Sa(a).forEach(function(e){d[e]=O(a[e],c)});Sa(b).forEach(function(e){if(!Ta(a,e)||Object.hasOwnProperty.call(a,e)&&Object.propertyIsEnumerable.call(a,e))if(Ta(a,e)&&c.isMergeableObject(b[e])){if(c.customMerge){var f=c.customMerge(e);f="function"===typeof f?f:ea}else f=ea;d[e]=f(a[e],b[e],c)}else d[e]=O(b[e],c)});return d}function ea(a,b,c){c=
|
||||||
|
c||{};c.arrayMerge=c.arrayMerge||pa;c.isMergeableObject=c.isMergeableObject||Eb;c.cloneUnlessOtherwiseSpecified=O;var d=Array.isArray(b),e=Array.isArray(a);return d!==e?O(b,c):d?c.arrayMerge(a,b,c):Db(a,b,c)}function Z(a){return"function"===typeof a}function qa(a){return"string"===typeof a}function Ua(a){let b=Object.getOwnPropertyNames(a.constructor.prototype);for(let c=0;c<b.length;c++){let d=b[c],e=a[d];"constructor"!==d&&"function"===typeof e&&(a[d]=e.bind(a))}return a}function Fb(a,b){return c=>
|
||||||
|
{if(b.isOpen()){let d=b.el&&c.currentTarget===b.el;(void 0!==a&&c.currentTarget.matches(a)||d)&&b.tour.next()}}}function Gb(a){let {event:b,selector:c}=a.options.advanceOn||{};if(b){let d=Fb(c,a),e;try{e=document.querySelector(c)}catch(f){}if(void 0===c||e)e?(e.addEventListener(b,d),a.on("destroy",()=>e.removeEventListener(b,d))):(document.body.addEventListener(b,d,!0),a.on("destroy",()=>document.body.removeEventListener(b,d,!0)));else return console.error(`No element was found for the selector supplied to advanceOn: ${c}`)}else return console.error("advanceOn was defined, but no event name was passed.")}
|
||||||
|
function M(a){return a?(a.nodeName||"").toLowerCase():null}function K(a){return null==a?window:"[object Window]"!==a.toString()?(a=a.ownerDocument)?a.defaultView||window:window:a}function fa(a){var b=K(a).Element;return a instanceof b||a instanceof Element}function F(a){var b=K(a).HTMLElement;return a instanceof b||a instanceof HTMLElement}function Ea(a){if("undefined"===typeof ShadowRoot)return!1;var b=K(a).ShadowRoot;return a instanceof b||a instanceof ShadowRoot}function N(a){return a.split("-")[0]}
|
||||||
|
function ha(a,b){void 0===b&&(b=!1);var c=a.getBoundingClientRect(),d=1,e=1;F(a)&&b&&(b=a.offsetHeight,a=a.offsetWidth,0<a&&(d=ia(c.width)/a||1),0<b&&(e=ia(c.height)/b||1));return{width:c.width/d,height:c.height/e,top:c.top/e,right:c.right/d,bottom:c.bottom/e,left:c.left/d,x:c.left/d,y:c.top/e}}function Fa(a){var b=ha(a),c=a.offsetWidth,d=a.offsetHeight;1>=Math.abs(b.width-c)&&(c=b.width);1>=Math.abs(b.height-d)&&(d=b.height);return{x:a.offsetLeft,y:a.offsetTop,width:c,height:d}}function Va(a,b){var c=
|
||||||
|
b.getRootNode&&b.getRootNode();if(a.contains(b))return!0;if(c&&Ea(c)){do{if(b&&a.isSameNode(b))return!0;b=b.parentNode||b.host}while(b)}return!1}function P(a){return K(a).getComputedStyle(a)}function U(a){return((fa(a)?a.ownerDocument:a.document)||window.document).documentElement}function wa(a){return"html"===M(a)?a:a.assignedSlot||a.parentNode||(Ea(a)?a.host:null)||U(a)}function Wa(a){return F(a)&&"fixed"!==P(a).position?a.offsetParent:null}function ra(a){for(var b=K(a),c=Wa(a);c&&0<=["table","td",
|
||||||
|
"th"].indexOf(M(c))&&"static"===P(c).position;)c=Wa(c);if(c&&("html"===M(c)||"body"===M(c)&&"static"===P(c).position))return b;if(!c)a:{c=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1===navigator.userAgent.indexOf("Trident")||!F(a)||"fixed"!==P(a).position)for(a=wa(a),Ea(a)&&(a=a.host);F(a)&&0>["html","body"].indexOf(M(a));){var d=P(a);if("none"!==d.transform||"none"!==d.perspective||"paint"===d.contain||-1!==["transform","perspective"].indexOf(d.willChange)||c&&"filter"===d.willChange||
|
||||||
|
c&&d.filter&&"none"!==d.filter){c=a;break a}else a=a.parentNode}c=null}return c||b}function Ga(a){return 0<=["top","bottom"].indexOf(a)?"x":"y"}function Xa(a){return Object.assign({},{top:0,right:0,bottom:0,left:0},a)}function Ya(a,b){return b.reduce(function(c,d){c[d]=a;return c},{})}function ja(a){return a.split("-")[1]}function Za(a){var b,c=a.popper,d=a.popperRect,e=a.placement,f=a.variation,g=a.offsets,l=a.position,m=a.gpuAcceleration,k=a.adaptive,p=a.roundOffsets,q=a.isFixed;a=g.x;a=void 0===
|
||||||
|
a?0:a;var n=g.y,r=void 0===n?0:n;n="function"===typeof p?p({x:a,y:r}):{x:a,y:r};a=n.x;r=n.y;n=g.hasOwnProperty("x");g=g.hasOwnProperty("y");var x="left",h="top",t=window;if(k){var v=ra(c),A="clientHeight",u="clientWidth";v===K(c)&&(v=U(c),"static"!==P(v).position&&"absolute"===l&&(A="scrollHeight",u="scrollWidth"));if("top"===e||("left"===e||"right"===e)&&"end"===f)h="bottom",r-=(q&&v===t&&t.visualViewport?t.visualViewport.height:v[A])-d.height,r*=m?1:-1;if("left"===e||("top"===e||"bottom"===e)&&
|
||||||
|
"end"===f)x="right",a-=(q&&v===t&&t.visualViewport?t.visualViewport.width:v[u])-d.width,a*=m?1:-1}c=Object.assign({position:l},k&&Hb);!0===p?(p=r,d=window.devicePixelRatio||1,a={x:ia(a*d)/d||0,y:ia(p*d)/d||0}):a={x:a,y:r};p=a;a=p.x;r=p.y;if(m){var w;return Object.assign({},c,(w={},w[h]=g?"0":"",w[x]=n?"0":"",w.transform=1>=(t.devicePixelRatio||1)?"translate("+a+"px, "+r+"px)":"translate3d("+a+"px, "+r+"px, 0)",w))}return Object.assign({},c,(b={},b[h]=g?r+"px":"",b[x]=n?a+"px":"",b.transform="",b))}
|
||||||
|
function xa(a){return a.replace(/left|right|bottom|top/g,function(b){return Ib[b]})}function $a(a){return a.replace(/start|end/g,function(b){return Jb[b]})}function Ha(a){a=K(a);return{scrollLeft:a.pageXOffset,scrollTop:a.pageYOffset}}function Ia(a){return ha(U(a)).left+Ha(a).scrollLeft}function Ja(a){a=P(a);return/auto|scroll|overlay|hidden/.test(a.overflow+a.overflowY+a.overflowX)}function ab(a){return 0<=["html","body","#document"].indexOf(M(a))?a.ownerDocument.body:F(a)&&Ja(a)?a:ab(wa(a))}function sa(a,
|
||||||
|
b){var c;void 0===b&&(b=[]);var d=ab(a);a=d===(null==(c=a.ownerDocument)?void 0:c.body);c=K(d);d=a?[c].concat(c.visualViewport||[],Ja(d)?d:[]):d;b=b.concat(d);return a?b:b.concat(sa(wa(d)))}function Ka(a){return Object.assign({},a,{left:a.x,top:a.y,right:a.x+a.width,bottom:a.y+a.height})}function bb(a,b){if("viewport"===b){b=K(a);var c=U(a);b=b.visualViewport;var d=c.clientWidth;c=c.clientHeight;var e=0,f=0;b&&(d=b.width,c=b.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(e=b.offsetLeft,
|
||||||
|
f=b.offsetTop));a={width:d,height:c,x:e+Ia(a),y:f};a=Ka(a)}else fa(b)?(a=ha(b),a.top+=b.clientTop,a.left+=b.clientLeft,a.bottom=a.top+b.clientHeight,a.right=a.left+b.clientWidth,a.width=b.clientWidth,a.height=b.clientHeight,a.x=a.left,a.y=a.top):(f=U(a),a=U(f),d=Ha(f),b=null==(c=f.ownerDocument)?void 0:c.body,c=L(a.scrollWidth,a.clientWidth,b?b.scrollWidth:0,b?b.clientWidth:0),e=L(a.scrollHeight,a.clientHeight,b?b.scrollHeight:0,b?b.clientHeight:0),f=-d.scrollLeft+Ia(f),d=-d.scrollTop,"rtl"===P(b||
|
||||||
|
a).direction&&(f+=L(a.clientWidth,b?b.clientWidth:0)-c),a=Ka({width:c,height:e,x:f,y:d}));return a}function Kb(a){var b=sa(wa(a)),c=0<=["absolute","fixed"].indexOf(P(a).position)&&F(a)?ra(a):a;return fa(c)?b.filter(function(d){return fa(d)&&Va(d,c)&&"body"!==M(d)}):[]}function Lb(a,b,c){b="clippingParents"===b?Kb(a):[].concat(b);c=[].concat(b,[c]);c=c.reduce(function(d,e){e=bb(a,e);d.top=L(e.top,d.top);d.right=V(e.right,d.right);d.bottom=V(e.bottom,d.bottom);d.left=L(e.left,d.left);return d},bb(a,
|
||||||
|
c[0]));c.width=c.right-c.left;c.height=c.bottom-c.top;c.x=c.left;c.y=c.top;return c}function cb(a){var b=a.reference,c=a.element,d=(a=a.placement)?N(a):null;a=a?ja(a):null;var e=b.x+b.width/2-c.width/2,f=b.y+b.height/2-c.height/2;switch(d){case "top":e={x:e,y:b.y-c.height};break;case "bottom":e={x:e,y:b.y+b.height};break;case "right":e={x:b.x+b.width,y:f};break;case "left":e={x:b.x-c.width,y:f};break;default:e={x:b.x,y:b.y}}d=d?Ga(d):null;if(null!=d)switch(f="y"===d?"height":"width",a){case "start":e[d]-=
|
||||||
|
b[f]/2-c[f]/2;break;case "end":e[d]+=b[f]/2-c[f]/2}return e}function ta(a,b){void 0===b&&(b={});var c=b;b=c.placement;b=void 0===b?a.placement:b;var d=c.boundary,e=void 0===d?"clippingParents":d;d=c.rootBoundary;var f=void 0===d?"viewport":d;d=c.elementContext;d=void 0===d?"popper":d;var g=c.altBoundary,l=void 0===g?!1:g;c=c.padding;c=void 0===c?0:c;c=Xa("number"!==typeof c?c:Ya(c,ua));g=a.rects.popper;l=a.elements[l?"popper"===d?"reference":"popper":d];e=Lb(fa(l)?l:l.contextElement||U(a.elements.popper),
|
||||||
|
e,f);f=ha(a.elements.reference);l=cb({reference:f,element:g,strategy:"absolute",placement:b});g=Ka(Object.assign({},g,l));f="popper"===d?g:f;var m={top:e.top-f.top+c.top,bottom:f.bottom-e.bottom+c.bottom,left:e.left-f.left+c.left,right:f.right-e.right+c.right};a=a.modifiersData.offset;if("popper"===d&&a){var k=a[b];Object.keys(m).forEach(function(p){var q=0<=["right","bottom"].indexOf(p)?1:-1,n=0<=["top","bottom"].indexOf(p)?"y":"x";m[p]+=k[n]*q})}return m}function Mb(a,b){void 0===b&&(b={});var c=
|
||||||
|
b.boundary,d=b.rootBoundary,e=b.padding,f=b.flipVariations,g=b.allowedAutoPlacements,l=void 0===g?db:g,m=ja(b.placement);b=m?f?eb:eb.filter(function(p){return ja(p)===m}):ua;f=b.filter(function(p){return 0<=l.indexOf(p)});0===f.length&&(f=b);var k=f.reduce(function(p,q){p[q]=ta(a,{placement:q,boundary:c,rootBoundary:d,padding:e})[N(q)];return p},{});return Object.keys(k).sort(function(p,q){return k[p]-k[q]})}function Nb(a){if("auto"===N(a))return[];var b=xa(a);return[$a(a),b,$a(b)]}function fb(a,
|
||||||
|
b,c){void 0===c&&(c={x:0,y:0});return{top:a.top-b.height-c.y,right:a.right-b.width+c.x,bottom:a.bottom-b.height+c.y,left:a.left-b.width-c.x}}function gb(a){return["top","right","bottom","left"].some(function(b){return 0<=a[b]})}function Ob(a,b,c){void 0===c&&(c=!1);var d=F(b),e;if(e=F(b)){var f=b.getBoundingClientRect();e=ia(f.width)/b.offsetWidth||1;f=ia(f.height)/b.offsetHeight||1;e=1!==e||1!==f}f=e;e=U(b);a=ha(a,f);f={scrollLeft:0,scrollTop:0};var g={x:0,y:0};if(d||!d&&!c){if("body"!==M(b)||Ja(e))f=
|
||||||
|
b!==K(b)&&F(b)?{scrollLeft:b.scrollLeft,scrollTop:b.scrollTop}:Ha(b);F(b)?(g=ha(b,!0),g.x+=b.clientLeft,g.y+=b.clientTop):e&&(g.x=Ia(e))}return{x:a.left+f.scrollLeft-g.x,y:a.top+f.scrollTop-g.y,width:a.width,height:a.height}}function Pb(a){function b(f){d.add(f.name);[].concat(f.requires||[],f.requiresIfExists||[]).forEach(function(g){d.has(g)||(g=c.get(g))&&b(g)});e.push(f)}var c=new Map,d=new Set,e=[];a.forEach(function(f){c.set(f.name,f)});a.forEach(function(f){d.has(f.name)||b(f)});return e}function Qb(a){var b=
|
||||||
|
Pb(a);return Rb.reduce(function(c,d){return c.concat(b.filter(function(e){return e.phase===d}))},[])}function Sb(a){var b;return function(){b||(b=new Promise(function(c){Promise.resolve().then(function(){b=void 0;c(a())})}));return b}}function Tb(a){var b=a.reduce(function(c,d){var e=c[d.name];c[d.name]=e?Object.assign({},e,d,{options:Object.assign({},e.options,d.options),data:Object.assign({},e.data,d.data)}):d;return c},{});return Object.keys(b).map(function(c){return b[c]})}function hb(){for(var a=
|
||||||
|
arguments.length,b=Array(a),c=0;c<a;c++)b[c]=arguments[c];return!b.some(function(d){return!(d&&"function"===typeof d.getBoundingClientRect)})}function La(){La=Object.assign?Object.assign.bind():function(a){for(var b=1;b<arguments.length;b++){var c=arguments[b],d;for(d in c)Object.prototype.hasOwnProperty.call(c,d)&&(a[d]=c[d])}return a};return La.apply(this,arguments)}function Ub(){return[{name:"applyStyles",fn(a){let {state:b}=a;Object.keys(b.elements).forEach(c=>{if("popper"===c){var d=b.attributes[c]||
|
||||||
|
{},e=b.elements[c];Object.assign(e.style,{position:"fixed",left:"50%",top:"50%",transform:"translate(-50%, -50%)"});Object.keys(d).forEach(f=>{let g=d[f];!1===g?e.removeAttribute(f):e.setAttribute(f,!0===g?"":g)})}})}},{name:"computeStyles",options:{adaptive:!1}}]}function Vb(a){let b=Ub(),c={placement:"top",strategy:"fixed",modifiers:[{name:"focusAfterRender",enabled:!0,phase:"afterWrite",fn(){setTimeout(()=>{a.el&&a.el.focus()},300)}}]};return c=La({},c,{modifiers:Array.from(new Set([...c.modifiers,
|
||||||
|
...b]))})}function ib(a){return qa(a)&&""!==a?"-"!==a.charAt(a.length-1)?`${a}-`:a:""}function Ma(){let a=Date.now();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,b=>{let c=(a+16*Math.random())%16|0;a=Math.floor(a/16);return("x"==b?c:c&3|8).toString(16)})}function Wb(a,b){let c={modifiers:[{name:"preventOverflow",options:{altAxis:!0,tether:!1}},{name:"focusAfterRender",enabled:!0,phase:"afterWrite",fn(){setTimeout(()=>{b.el&&b.el.focus()},300)}}],strategy:"absolute"};void 0!==a&&null!==
|
||||||
|
a&&a.element&&a.on?c.placement=a.on:c=Vb(b);(a=b.tour&&b.tour.options&&b.tour.options.defaultStepOptions)&&(c=jb(a,c));return c=jb(b.options,c)}function jb(a,b){if(a.popperOptions){let c=Object.assign({},b,a.popperOptions);if(a.popperOptions.modifiers&&0<a.popperOptions.modifiers.length){let d=a.popperOptions.modifiers.map(e=>e.name);b=b.modifiers.filter(e=>!d.includes(e.name));c.modifiers=Array.from(new Set([...b,...a.popperOptions.modifiers]))}return c}return b}function G(){}function Xb(a,b){for(let c in b)a[c]=
|
||||||
|
b[c];return a}function ka(a){return a()}function kb(a){return"function"===typeof a}function Q(a,b){return a!=a?b==b:a!==b||a&&"object"===typeof a||"function"===typeof a}function H(a){a.parentNode.removeChild(a)}function lb(a){return document.createElementNS("http://www.w3.org/2000/svg",a)}function ya(a,b,c,d){a.addEventListener(b,c,d);return()=>a.removeEventListener(b,c,d)}function B(a,b,c){null==c?a.removeAttribute(b):a.getAttribute(b)!==c&&a.setAttribute(b,c)}function mb(a,b){let c=Object.getOwnPropertyDescriptors(a.__proto__);
|
||||||
|
for(let d in b)null==b[d]?a.removeAttribute(d):"style"===d?a.style.cssText=b[d]:"__value"===d?a.value=a[d]=b[d]:c[d]&&c[d].set?a[d]=b[d]:B(a,d,b[d])}function la(a,b,c){a.classList[c?"add":"remove"](b)}function za(){if(!R)throw Error("Function called outside component initialization");return R}function Na(a){Aa.push(a)}function nb(){let a=R;do{for(;Ba<va.length;){var b=va[Ba];Ba++;R=b;b=b.$$;if(null!==b.fragment){b.update();b.before_update.forEach(ka);var c=b.dirty;b.dirty=[-1];b.fragment&&b.fragment.p(b.ctx,
|
||||||
|
c);b.after_update.forEach(Na)}}R=null;for(Ba=va.length=0;ma.length;)ma.pop()();for(b=0;b<Aa.length;b+=1)c=Aa[b],Oa.has(c)||(Oa.add(c),c());Aa.length=0}while(va.length);for(;ob.length;)ob.pop()();Pa=!1;Oa.clear();R=a}function aa(){ba={r:0,c:[],p:ba}}function ca(){ba.r||ba.c.forEach(ka);ba=ba.p}function z(a,b){a&&a.i&&(Ca.delete(a),a.i(b))}function C(a,b,c,d){a&&a.o&&!Ca.has(a)&&(Ca.add(a),ba.c.push(()=>{Ca.delete(a);d&&(c&&a.d(1),d())}),a.o(b))}function da(a){a&&a.c()}function W(a,b,c,d){let {fragment:e,
|
||||||
|
on_mount:f,on_destroy:g,after_update:l}=a.$$;e&&e.m(b,c);d||Na(()=>{let m=f.map(ka).filter(kb);g?g.push(...m):m.forEach(ka);a.$$.on_mount=[]});l.forEach(Na)}function X(a,b){a=a.$$;null!==a.fragment&&(a.on_destroy.forEach(ka),a.fragment&&a.fragment.d(b),a.on_destroy=a.fragment=null,a.ctx=[])}function S(a,b,c,d,e,f,g,l){void 0===l&&(l=[-1]);let m=R;R=a;let k=a.$$={fragment:null,ctx:null,props:f,update:G,not_equal:e,bound:Object.create(null),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],
|
||||||
|
after_update:[],context:new Map(b.context||(m?m.$$.context:[])),callbacks:Object.create(null),dirty:l,skip_bound:!1,root:b.target||m.$$.root};g&&g(k.root);let p=!1;k.ctx=c?c(a,b.props||{},function(q,n){let r=(2>=arguments.length?0:arguments.length-2)?2>=arguments.length?void 0:arguments[2]:n;if(k.ctx&&e(k.ctx[q],k.ctx[q]=r)){if(!k.skip_bound&&k.bound[q])k.bound[q](r);p&&(-1===a.$$.dirty[0]&&(va.push(a),Pa||(Pa=!0,Yb.then(nb)),a.$$.dirty.fill(0)),a.$$.dirty[q/31|0]|=1<<q%31)}return n}):[];k.update();
|
||||||
|
p=!0;k.before_update.forEach(ka);k.fragment=d?d(k.ctx):!1;b.target&&(b.hydrate?(c=Array.from(b.target.childNodes),k.fragment&&k.fragment.l(c),c.forEach(H)):k.fragment&&k.fragment.c(),b.intro&&z(a.$$.fragment),W(a,b.target,b.anchor,b.customElement),nb());R=m}function Zb(a){let b,c,d,e,f;return{c(){b=document.createElement("button");B(b,"aria-label",c=a[3]?a[3]:null);B(b,"class",d=`${a[1]||""} shepherd-button ${a[4]?"shepherd-button-secondary":""}`);b.disabled=a[2];B(b,"tabindex","0")},m(g,l){g.insertBefore(b,
|
||||||
|
l||null);b.innerHTML=a[5];e||(f=ya(b,"click",function(){kb(a[0])&&a[0].apply(this,arguments)}),e=!0)},p(g,l){[l]=l;a=g;l&32&&(b.innerHTML=a[5]);l&8&&c!==(c=a[3]?a[3]:null)&&B(b,"aria-label",c);l&18&&d!==(d=`${a[1]||""} shepherd-button ${a[4]?"shepherd-button-secondary":""}`)&&B(b,"class",d);l&4&&(b.disabled=a[2])},i:G,o:G,d(g){g&&H(b);e=!1;f()}}}function $b(a,b,c){function d(n){return Z(n)?n.call(f):n}let {config:e,step:f}=b,g,l,m,k,p,q;a.$$set=n=>{"config"in n&&c(6,e=n.config);"step"in n&&c(7,f=
|
||||||
|
n.step)};a.$$.update=()=>{a.$$.dirty&192&&(c(0,g=e.action?e.action.bind(f.tour):null),c(1,l=e.classes),c(2,m=e.disabled?d(e.disabled):!1),c(3,k=e.label?d(e.label):null),c(4,p=e.secondary),c(5,q=e.text?d(e.text):null))};return[g,l,m,k,p,q,e,f]}function pb(a,b,c){a=a.slice();a[2]=b[c];return a}function qb(a){let b,c,d=a[1],e=[];for(let g=0;g<d.length;g+=1)e[g]=rb(pb(a,d,g));let f=g=>C(e[g],1,1,()=>{e[g]=null});return{c(){for(let g=0;g<e.length;g+=1)e[g].c();b=document.createTextNode("")},m(g,l){for(let m=
|
||||||
|
0;m<e.length;m+=1)e[m].m(g,l);g.insertBefore(b,l||null);c=!0},p(g,l){if(l&3){d=g[1];let m;for(m=0;m<d.length;m+=1){let k=pb(g,d,m);e[m]?(e[m].p(k,l),z(e[m],1)):(e[m]=rb(k),e[m].c(),z(e[m],1),e[m].m(b.parentNode,b))}aa();for(m=d.length;m<e.length;m+=1)f(m);ca()}},i(g){if(!c){for(g=0;g<d.length;g+=1)z(e[g]);c=!0}},o(g){e=e.filter(Boolean);for(g=0;g<e.length;g+=1)C(e[g]);c=!1},d(g){var l=e;for(let m=0;m<l.length;m+=1)l[m]&&l[m].d(g);g&&H(b)}}}function rb(a){let b,c;b=new ac({props:{config:a[2],step:a[0]}});
|
||||||
|
return{c(){da(b.$$.fragment)},m(d,e){W(b,d,e);c=!0},p(d,e){let f={};e&2&&(f.config=d[2]);e&1&&(f.step=d[0]);b.$set(f)},i(d){c||(z(b.$$.fragment,d),c=!0)},o(d){C(b.$$.fragment,d);c=!1},d(d){X(b,d)}}}function bc(a){let b,c,d=a[1]&&qb(a);return{c(){b=document.createElement("footer");d&&d.c();B(b,"class","shepherd-footer")},m(e,f){e.insertBefore(b,f||null);d&&d.m(b,null);c=!0},p(e,f){[f]=f;e[1]?d?(d.p(e,f),f&2&&z(d,1)):(d=qb(e),d.c(),z(d,1),d.m(b,null)):d&&(aa(),C(d,1,1,()=>{d=null}),ca())},i(e){c||(z(d),
|
||||||
|
c=!0)},o(e){C(d);c=!1},d(e){e&&H(b);d&&d.d()}}}function cc(a,b,c){let d,{step:e}=b;a.$$set=f=>{"step"in f&&c(0,e=f.step)};a.$$.update=()=>{a.$$.dirty&1&&c(1,d=e.options.buttons)};return[e,d]}function dc(a){let b,c,d,e,f;return{c(){b=document.createElement("button");c=document.createElement("span");c.textContent="\u00d7";B(c,"aria-hidden","true");B(b,"aria-label",d=a[0].label?a[0].label:"Close Tour");B(b,"class","shepherd-cancel-icon");B(b,"type","button")},m(g,l){g.insertBefore(b,l||null);b.appendChild(c);
|
||||||
|
e||(f=ya(b,"click",a[1]),e=!0)},p(g,l){[l]=l;l&1&&d!==(d=g[0].label?g[0].label:"Close Tour")&&B(b,"aria-label",d)},i:G,o:G,d(g){g&&H(b);e=!1;f()}}}function ec(a,b,c){let {cancelIcon:d,step:e}=b;a.$$set=f=>{"cancelIcon"in f&&c(0,d=f.cancelIcon);"step"in f&&c(2,e=f.step)};return[d,f=>{f.preventDefault();e.cancel()},e]}function fc(a){let b;return{c(){b=document.createElement("h3");B(b,"id",a[1]);B(b,"class","shepherd-title")},m(c,d){c.insertBefore(b,d||null);a[3](b)},p(c,d){[d]=d;d&2&&B(b,"id",c[1])},
|
||||||
|
i:G,o:G,d(c){c&&H(b);a[3](null)}}}function gc(a,b,c){let {labelId:d,element:e,title:f}=b;za().$$.after_update.push(()=>{Z(f)&&c(2,f=f());c(0,e.innerHTML=f,e)});a.$$set=g=>{"labelId"in g&&c(1,d=g.labelId);"element"in g&&c(0,e=g.element);"title"in g&&c(2,f=g.title)};return[e,d,f,function(g){ma[g?"unshift":"push"](()=>{e=g;c(0,e)})}]}function sb(a){let b,c;b=new hc({props:{labelId:a[0],title:a[2]}});return{c(){da(b.$$.fragment)},m(d,e){W(b,d,e);c=!0},p(d,e){let f={};e&1&&(f.labelId=d[0]);e&4&&(f.title=
|
||||||
|
d[2]);b.$set(f)},i(d){c||(z(b.$$.fragment,d),c=!0)},o(d){C(b.$$.fragment,d);c=!1},d(d){X(b,d)}}}function tb(a){let b,c;b=new ic({props:{cancelIcon:a[3],step:a[1]}});return{c(){da(b.$$.fragment)},m(d,e){W(b,d,e);c=!0},p(d,e){let f={};e&8&&(f.cancelIcon=d[3]);e&2&&(f.step=d[1]);b.$set(f)},i(d){c||(z(b.$$.fragment,d),c=!0)},o(d){C(b.$$.fragment,d);c=!1},d(d){X(b,d)}}}function jc(a){let b,c,d,e=a[2]&&sb(a),f=a[3]&&a[3].enabled&&tb(a);return{c(){b=document.createElement("header");e&&e.c();c=document.createTextNode(" ");
|
||||||
|
f&&f.c();B(b,"class","shepherd-header")},m(g,l){g.insertBefore(b,l||null);e&&e.m(b,null);b.appendChild(c);f&&f.m(b,null);d=!0},p(g,l){[l]=l;g[2]?e?(e.p(g,l),l&4&&z(e,1)):(e=sb(g),e.c(),z(e,1),e.m(b,c)):e&&(aa(),C(e,1,1,()=>{e=null}),ca());g[3]&&g[3].enabled?f?(f.p(g,l),l&8&&z(f,1)):(f=tb(g),f.c(),z(f,1),f.m(b,null)):f&&(aa(),C(f,1,1,()=>{f=null}),ca())},i(g){d||(z(e),z(f),d=!0)},o(g){C(e);C(f);d=!1},d(g){g&&H(b);e&&e.d();f&&f.d()}}}function kc(a,b,c){let {labelId:d,step:e}=b,f,g;a.$$set=l=>{"labelId"in
|
||||||
|
l&&c(0,d=l.labelId);"step"in l&&c(1,e=l.step)};a.$$.update=()=>{a.$$.dirty&2&&(c(2,f=e.options.title),c(3,g=e.options.cancelIcon))};return[d,e,f,g]}function lc(a){let b;return{c(){b=document.createElement("div");B(b,"class","shepherd-text");B(b,"id",a[1])},m(c,d){c.insertBefore(b,d||null);a[3](b)},p(c,d){[d]=d;d&2&&B(b,"id",c[1])},i:G,o:G,d(c){c&&H(b);a[3](null)}}}function mc(a,b,c){let {descriptionId:d,element:e,step:f}=b;za().$$.after_update.push(()=>{let {text:g}=f.options;Z(g)&&(g=g.call(f));
|
||||||
|
g instanceof HTMLElement?e.appendChild(g):c(0,e.innerHTML=g,e)});a.$$set=g=>{"descriptionId"in g&&c(1,d=g.descriptionId);"element"in g&&c(0,e=g.element);"step"in g&&c(2,f=g.step)};return[e,d,f,function(g){ma[g?"unshift":"push"](()=>{e=g;c(0,e)})}]}function ub(a){let b,c;b=new nc({props:{labelId:a[1],step:a[2]}});return{c(){da(b.$$.fragment)},m(d,e){W(b,d,e);c=!0},p(d,e){let f={};e&2&&(f.labelId=d[1]);e&4&&(f.step=d[2]);b.$set(f)},i(d){c||(z(b.$$.fragment,d),c=!0)},o(d){C(b.$$.fragment,d);c=!1},d(d){X(b,
|
||||||
|
d)}}}function vb(a){let b,c;b=new oc({props:{descriptionId:a[0],step:a[2]}});return{c(){da(b.$$.fragment)},m(d,e){W(b,d,e);c=!0},p(d,e){let f={};e&1&&(f.descriptionId=d[0]);e&4&&(f.step=d[2]);b.$set(f)},i(d){c||(z(b.$$.fragment,d),c=!0)},o(d){C(b.$$.fragment,d);c=!1},d(d){X(b,d)}}}function wb(a){let b,c;b=new pc({props:{step:a[2]}});return{c(){da(b.$$.fragment)},m(d,e){W(b,d,e);c=!0},p(d,e){let f={};e&4&&(f.step=d[2]);b.$set(f)},i(d){c||(z(b.$$.fragment,d),c=!0)},o(d){C(b.$$.fragment,d);c=!1},d(d){X(b,
|
||||||
|
d)}}}function qc(a){let b,c=void 0!==a[2].options.title||a[2].options.cancelIcon&&a[2].options.cancelIcon.enabled,d,e=void 0!==a[2].options.text,f,g=Array.isArray(a[2].options.buttons)&&a[2].options.buttons.length,l,m=c&&ub(a),k=e&&vb(a),p=g&&wb(a);return{c(){b=document.createElement("div");m&&m.c();d=document.createTextNode(" ");k&&k.c();f=document.createTextNode(" ");p&&p.c();B(b,"class","shepherd-content")},m(q,n){q.insertBefore(b,n||null);m&&m.m(b,null);b.appendChild(d);k&&k.m(b,null);b.appendChild(f);
|
||||||
|
p&&p.m(b,null);l=!0},p(q,n){[n]=n;n&4&&(c=void 0!==q[2].options.title||q[2].options.cancelIcon&&q[2].options.cancelIcon.enabled);c?m?(m.p(q,n),n&4&&z(m,1)):(m=ub(q),m.c(),z(m,1),m.m(b,d)):m&&(aa(),C(m,1,1,()=>{m=null}),ca());n&4&&(e=void 0!==q[2].options.text);e?k?(k.p(q,n),n&4&&z(k,1)):(k=vb(q),k.c(),z(k,1),k.m(b,f)):k&&(aa(),C(k,1,1,()=>{k=null}),ca());n&4&&(g=Array.isArray(q[2].options.buttons)&&q[2].options.buttons.length);g?p?(p.p(q,n),n&4&&z(p,1)):(p=wb(q),p.c(),z(p,1),p.m(b,null)):p&&(aa(),
|
||||||
|
C(p,1,1,()=>{p=null}),ca())},i(q){l||(z(m),z(k),z(p),l=!0)},o(q){C(m);C(k);C(p);l=!1},d(q){q&&H(b);m&&m.d();k&&k.d();p&&p.d()}}}function rc(a,b,c){let {descriptionId:d,labelId:e,step:f}=b;a.$$set=g=>{"descriptionId"in g&&c(0,d=g.descriptionId);"labelId"in g&&c(1,e=g.labelId);"step"in g&&c(2,f=g.step)};return[d,e,f]}function xb(a){let b;return{c(){b=document.createElement("div");B(b,"class","shepherd-arrow");B(b,"data-popper-arrow","")},m(c,d){c.insertBefore(b,d||null)},d(c){c&&H(b)}}}function sc(a){let b,
|
||||||
|
c,d,e,f,g,l,m,k=a[4].options.arrow&&a[4].options.attachTo&&a[4].options.attachTo.element&&a[4].options.attachTo.on&&xb();d=new tc({props:{descriptionId:a[2],labelId:a[3],step:a[4]}});let p=[{"aria-describedby":e=void 0!==a[4].options.text?a[2]:null},{"aria-labelledby":f=a[4].options.title?a[3]:null},a[1],{role:"dialog"},{tabindex:"0"}],q={};for(let n=0;n<p.length;n+=1)q=Xb(q,p[n]);return{c(){b=document.createElement("div");k&&k.c();c=document.createTextNode(" ");da(d.$$.fragment);mb(b,q);la(b,"shepherd-has-cancel-icon",
|
||||||
|
a[5]);la(b,"shepherd-has-title",a[6]);la(b,"shepherd-element",!0)},m(n,r){n.insertBefore(b,r||null);k&&k.m(b,null);b.appendChild(c);W(d,b,null);a[13](b);g=!0;l||(m=ya(b,"keydown",a[7]),l=!0)},p(n,r){var [x]=r;n[4].options.arrow&&n[4].options.attachTo&&n[4].options.attachTo.element&&n[4].options.attachTo.on?k||(k=xb(),k.c(),k.m(b,c)):k&&(k.d(1),k=null);r={};x&4&&(r.descriptionId=n[2]);x&8&&(r.labelId=n[3]);x&16&&(r.step=n[4]);d.$set(r);r=b;x=[(!g||x&20&&e!==(e=void 0!==n[4].options.text?n[2]:null))&&
|
||||||
|
{"aria-describedby":e},(!g||x&24&&f!==(f=n[4].options.title?n[3]:null))&&{"aria-labelledby":f},x&2&&n[1],{role:"dialog"},{tabindex:"0"}];let h={},t={},v={$$scope:1},A=p.length;for(;A--;){let u=p[A],w=x[A];if(w){for(let y in u)y in w||(t[y]=1);for(let y in w)v[y]||(h[y]=w[y],v[y]=1);p[A]=w}else for(let y in u)v[y]=1}for(let u in t)u in h||(h[u]=void 0);mb(r,q=h);la(b,"shepherd-has-cancel-icon",n[5]);la(b,"shepherd-has-title",n[6]);la(b,"shepherd-element",!0)},i(n){g||(z(d.$$.fragment,n),g=!0)},o(n){C(d.$$.fragment,
|
||||||
|
n);g=!1},d(n){n&&H(b);k&&k.d();X(d);a[13](null);l=!1;m()}}}function yb(a){return a.split(" ").filter(b=>!!b.length)}function uc(a,b,c){let {classPrefix:d,element:e,descriptionId:f,firstFocusableElement:g,focusableElements:l,labelId:m,lastFocusableElement:k,step:p,dataStepId:q}=b,n,r,x;za().$$.on_mount.push(()=>{c(1,q={[`data-${d}shepherd-step-id`]:p.id});c(9,l=e.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'));
|
||||||
|
c(8,g=l[0]);c(10,k=l[l.length-1])});za().$$.after_update.push(()=>{if(x!==p.options.classes){var h=x;qa(h)&&(h=yb(h),h.length&&e.classList.remove(...h));h=x=p.options.classes;qa(h)&&(h=yb(h),h.length&&e.classList.add(...h))}});a.$$set=h=>{"classPrefix"in h&&c(11,d=h.classPrefix);"element"in h&&c(0,e=h.element);"descriptionId"in h&&c(2,f=h.descriptionId);"firstFocusableElement"in h&&c(8,g=h.firstFocusableElement);"focusableElements"in h&&c(9,l=h.focusableElements);"labelId"in h&&c(3,m=h.labelId);"lastFocusableElement"in
|
||||||
|
h&&c(10,k=h.lastFocusableElement);"step"in h&&c(4,p=h.step);"dataStepId"in h&&c(1,q=h.dataStepId)};a.$$.update=()=>{a.$$.dirty&16&&(c(5,n=p.options&&p.options.cancelIcon&&p.options.cancelIcon.enabled),c(6,r=p.options&&p.options.title))};return[e,q,f,m,p,n,r,h=>{const {tour:t}=p;switch(h.keyCode){case 9:if(0===l.length){h.preventDefault();break}if(h.shiftKey){if(document.activeElement===g||document.activeElement.classList.contains("shepherd-element"))h.preventDefault(),k.focus()}else document.activeElement===
|
||||||
|
k&&(h.preventDefault(),g.focus());break;case 27:t.options.exitOnEsc&&p.cancel();break;case 37:t.options.keyboardNavigation&&t.back();break;case 39:t.options.keyboardNavigation&&t.next()}},g,l,k,d,()=>e,function(h){ma[h?"unshift":"push"](()=>{e=h;c(0,e)})}]}function vc(a){a&&({steps:a}=a,a.forEach(b=>{b.options&&!1===b.options.canClickTarget&&b.options.attachTo&&b.target instanceof HTMLElement&&b.target.classList.remove("shepherd-target-click-disabled")}))}function wc(a){let b,c,d,e,f;return{c(){b=
|
||||||
|
lb("svg");c=lb("path");B(c,"d",a[2]);B(b,"class",d=`${a[1]?"shepherd-modal-is-visible":""} shepherd-modal-overlay-container`)},m(g,l){g.insertBefore(b,l||null);b.appendChild(c);a[11](b);e||(f=ya(b,"touchmove",a[3]),e=!0)},p(g,l){[l]=l;l&4&&B(c,"d",g[2]);l&2&&d!==(d=`${g[1]?"shepherd-modal-is-visible":""} shepherd-modal-overlay-container`)&&B(b,"class",d)},i:G,o:G,d(g){g&&H(b);a[11](null);e=!1;f()}}}function zb(a){if(!a)return null;let b=a instanceof HTMLElement&&window.getComputedStyle(a).overflowY;
|
||||||
|
return"hidden"!==b&&"visible"!==b&&a.scrollHeight>=a.clientHeight?a:zb(a.parentElement)}function xc(a,b,c){function d(){c(4,p={width:0,height:0,x:0,y:0,r:0})}function e(){c(1,q=!1);l()}function f(h,t,v,A){void 0===h&&(h=0);void 0===t&&(t=0);if(A){var u=A.getBoundingClientRect();let y=u.y||u.top;u=u.bottom||y+u.height;if(v){var w=v.getBoundingClientRect();v=w.y||w.top;w=w.bottom||v+w.height;y=Math.max(y,v);u=Math.min(u,w)}let {y:Y,height:E}={y,height:Math.max(u-y,0)},{x:I,width:D,left:na}=A.getBoundingClientRect();
|
||||||
|
c(4,p={width:D+2*h,height:E+2*h,x:(I||na)-h,y:Y-h,r:t})}else d()}function g(){c(1,q=!0)}function l(){n&&(cancelAnimationFrame(n),n=void 0);window.removeEventListener("touchmove",x,{passive:!1})}function m(h){let {modalOverlayOpeningPadding:t,modalOverlayOpeningRadius:v}=h.options,A=zb(h.target),u=()=>{n=void 0;f(t,v,A,h.target);n=requestAnimationFrame(u)};u();window.addEventListener("touchmove",x,{passive:!1})}let {element:k,openingProperties:p}=b;Ma();let q=!1,n=void 0,r;d();let x=h=>{h.preventDefault()};
|
||||||
|
a.$$set=h=>{"element"in h&&c(0,k=h.element);"openingProperties"in h&&c(4,p=h.openingProperties)};a.$$.update=()=>{if(a.$$.dirty&16){let {width:h,height:t,x:v=0,y:A=0,r:u=0}=p,{innerWidth:w,innerHeight:y}=window;c(2,r=`M${w},${y}\
|
||||||
|
H0\
|
||||||
|
V0\
|
||||||
|
H${w}\
|
||||||
|
V${y}\
|
||||||
|
Z\
|
||||||
|
M${v+u},${A}\
|
||||||
|
a${u},${u},0,0,0-${u},${u}\
|
||||||
|
V${t+A-u}\
|
||||||
|
a${u},${u},0,0,0,${u},${u}\
|
||||||
|
H${h+v-u}\
|
||||||
|
a${u},${u},0,0,0,${u}-${u}\
|
||||||
|
V${A+u}\
|
||||||
|
a${u},${u},0,0,0-${u}-${u}\
|
||||||
|
Z`)}};return[k,q,r,h=>{h.stopPropagation()},p,()=>k,d,e,f,function(h){l();h.tour.options.useModalOverlay?(m(h),g()):e()},g,function(h){ma[h?"unshift":"push"](()=>{k=h;c(0,k)})}]}var Eb=function(a){var b;if(b=!!a&&"object"===typeof a)b=Object.prototype.toString.call(a),b=!("[object RegExp]"===b||"[object Date]"===b||a.$$typeof===yc);return b},yc="function"===typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;ea.all=function(a,b){if(!Array.isArray(a))throw Error("first argument should be an array");
|
||||||
|
return a.reduce(function(c,d){return ea(c,d,b)},{})};var zc=ea;class Qa{on(a,b,c,d){void 0===d&&(d=!1);void 0===this.bindings&&(this.bindings={});void 0===this.bindings[a]&&(this.bindings[a]=[]);this.bindings[a].push({handler:b,ctx:c,once:d});return this}once(a,b,c){return this.on(a,b,c,!0)}off(a,b){if(void 0===this.bindings||void 0===this.bindings[a])return this;void 0===b?delete this.bindings[a]:this.bindings[a].forEach((c,d)=>{c.handler===b&&this.bindings[a].splice(d,1)});return this}trigger(a){for(var b=
|
||||||
|
arguments.length,c=Array(1<b?b-1:0),d=1;d<b;d++)c[d-1]=arguments[d];void 0!==this.bindings&&this.bindings[a]&&this.bindings[a].forEach((e,f)=>{let {ctx:g,handler:l,once:m}=e;l.apply(g||this,c);m&&this.bindings[a].splice(f,1)});return this}}var ua=["top","bottom","right","left"],eb=ua.reduce(function(a,b){return a.concat([b+"-start",b+"-end"])},[]),db=[].concat(ua,["auto"]).reduce(function(a,b){return a.concat([b,b+"-start",b+"-end"])},[]),Rb="beforeRead read afterRead beforeMain main afterMain beforeWrite write afterWrite".split(" "),
|
||||||
|
L=Math.max,V=Math.min,ia=Math.round,Hb={top:"auto",right:"auto",bottom:"auto",left:"auto"},Da={passive:!0},Ib={left:"right",right:"left",bottom:"top",top:"bottom"},Jb={start:"end",end:"start"},Ab={placement:"bottom",modifiers:[],strategy:"absolute"},Ac=function(a){void 0===a&&(a={});var b=a.defaultModifiers,c=void 0===b?[]:b;a=a.defaultOptions;var d=void 0===a?Ab:a;return function(e,f,g){function l(){k.orderedModifiers.forEach(function(r){var x=r.name,h=r.options;h=void 0===h?{}:h;r=r.effect;"function"===
|
||||||
|
typeof r&&(x=r({state:k,name:x,instance:n,options:h}),p.push(x||function(){}))})}function m(){p.forEach(function(r){return r()});p=[]}void 0===g&&(g=d);var k={placement:"bottom",orderedModifiers:[],options:Object.assign({},Ab,d),modifiersData:{},elements:{reference:e,popper:f},attributes:{},styles:{}},p=[],q=!1,n={state:k,setOptions:function(r){r="function"===typeof r?r(k.options):r;m();k.options=Object.assign({},d,k.options,r);k.scrollParents={reference:fa(e)?sa(e):e.contextElement?sa(e.contextElement):
|
||||||
|
[],popper:sa(f)};r=Qb(Tb([].concat(c,k.options.modifiers)));k.orderedModifiers=r.filter(function(x){return x.enabled});l();return n.update()},forceUpdate:function(){if(!q){var r=k.elements,x=r.reference;r=r.popper;if(hb(x,r))for(k.rects={reference:Ob(x,ra(r),"fixed"===k.options.strategy),popper:Fa(r)},k.reset=!1,k.placement=k.options.placement,k.orderedModifiers.forEach(function(v){return k.modifiersData[v.name]=Object.assign({},v.data)}),x=0;x<k.orderedModifiers.length;x++)if(!0===k.reset)k.reset=
|
||||||
|
!1,x=-1;else{var h=k.orderedModifiers[x];r=h.fn;var t=h.options;t=void 0===t?{}:t;h=h.name;"function"===typeof r&&(k=r({state:k,options:t,name:h,instance:n})||k)}}},update:Sb(function(){return new Promise(function(r){n.forceUpdate();r(k)})}),destroy:function(){m();q=!0}};if(!hb(e,f))return n;n.setOptions(g).then(function(r){if(!q&&g.onFirstUpdate)g.onFirstUpdate(r)});return n}}({defaultModifiers:[{name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(a){var b=a.state,c=a.instance;
|
||||||
|
a=a.options;var d=a.scroll,e=void 0===d?!0:d;a=a.resize;var f=void 0===a?!0:a,g=K(b.elements.popper),l=[].concat(b.scrollParents.reference,b.scrollParents.popper);e&&l.forEach(function(m){m.addEventListener("scroll",c.update,Da)});f&&g.addEventListener("resize",c.update,Da);return function(){e&&l.forEach(function(m){m.removeEventListener("scroll",c.update,Da)});f&&g.removeEventListener("resize",c.update,Da)}},data:{}},{name:"popperOffsets",enabled:!0,phase:"read",fn:function(a){var b=a.state;b.modifiersData[a.name]=
|
||||||
|
cb({reference:b.rects.reference,element:b.rects.popper,strategy:"absolute",placement:b.placement})},data:{}},{name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(a){var b=a.state,c=a.options;a=c.gpuAcceleration;a=void 0===a?!0:a;var d=c.adaptive;d=void 0===d?!0:d;c=c.roundOffsets;c=void 0===c?!0:c;a={placement:N(b.placement),variation:ja(b.placement),popper:b.elements.popper,popperRect:b.rects.popper,gpuAcceleration:a,isFixed:"fixed"===b.options.strategy};null!=b.modifiersData.popperOffsets&&
|
||||||
|
(b.styles.popper=Object.assign({},b.styles.popper,Za(Object.assign({},a,{offsets:b.modifiersData.popperOffsets,position:b.options.strategy,adaptive:d,roundOffsets:c}))));null!=b.modifiersData.arrow&&(b.styles.arrow=Object.assign({},b.styles.arrow,Za(Object.assign({},a,{offsets:b.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:c}))));b.attributes.popper=Object.assign({},b.attributes.popper,{"data-popper-placement":b.placement})},data:{}},{name:"applyStyles",enabled:!0,phase:"write",
|
||||||
|
fn:function(a){var b=a.state;Object.keys(b.elements).forEach(function(c){var d=b.styles[c]||{},e=b.attributes[c]||{},f=b.elements[c];F(f)&&M(f)&&(Object.assign(f.style,d),Object.keys(e).forEach(function(g){var l=e[g];!1===l?f.removeAttribute(g):f.setAttribute(g,!0===l?"":l)}))})},effect:function(a){var b=a.state,c={popper:{position:b.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};Object.assign(b.elements.popper.style,c.popper);b.styles=c;b.elements.arrow&&
|
||||||
|
Object.assign(b.elements.arrow.style,c.arrow);return function(){Object.keys(b.elements).forEach(function(d){var e=b.elements[d],f=b.attributes[d]||{};d=Object.keys(b.styles.hasOwnProperty(d)?b.styles[d]:c[d]).reduce(function(g,l){g[l]="";return g},{});F(e)&&M(e)&&(Object.assign(e.style,d),Object.keys(f).forEach(function(g){e.removeAttribute(g)}))})}},requires:["computeStyles"]},{name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(a){var b=a.state,c=a.name;a=a.options.offset;
|
||||||
|
var d=void 0===a?[0,0]:a;a=db.reduce(function(g,l){var m=b.rects;var k=N(l);var p=0<=["left","top"].indexOf(k)?-1:1,q="function"===typeof d?d(Object.assign({},m,{placement:l})):d;m=q[0];q=q[1];m=m||0;q=(q||0)*p;k=0<=["left","right"].indexOf(k)?{x:q,y:m}:{x:m,y:q};g[l]=k;return g},{});var e=a[b.placement],f=e.x;e=e.y;null!=b.modifiersData.popperOffsets&&(b.modifiersData.popperOffsets.x+=f,b.modifiersData.popperOffsets.y+=e);b.modifiersData[c]=a}},{name:"flip",enabled:!0,phase:"main",fn:function(a){var b=
|
||||||
|
a.state,c=a.options;a=a.name;if(!b.modifiersData[a]._skip){var d=c.mainAxis;d=void 0===d?!0:d;var e=c.altAxis;e=void 0===e?!0:e;var f=c.fallbackPlacements,g=c.padding,l=c.boundary,m=c.rootBoundary,k=c.altBoundary,p=c.flipVariations,q=void 0===p?!0:p,n=c.allowedAutoPlacements;c=b.options.placement;p=N(c);f=f||(p!==c&&q?Nb(c):[xa(c)]);var r=[c].concat(f).reduce(function(E,I){return E.concat("auto"===N(I)?Mb(b,{placement:I,boundary:l,rootBoundary:m,padding:g,flipVariations:q,allowedAutoPlacements:n}):
|
||||||
|
I)},[]);c=b.rects.reference;f=b.rects.popper;var x=new Map;p=!0;for(var h=r[0],t=0;t<r.length;t++){var v=r[t],A=N(v),u="start"===ja(v),w=0<=["top","bottom"].indexOf(A),y=w?"width":"height",Y=ta(b,{placement:v,boundary:l,rootBoundary:m,altBoundary:k,padding:g});u=w?u?"right":"left":u?"bottom":"top";c[y]>f[y]&&(u=xa(u));y=xa(u);w=[];d&&w.push(0>=Y[A]);e&&w.push(0>=Y[u],0>=Y[y]);if(w.every(function(E){return E})){h=v;p=!1;break}x.set(v,w)}if(p)for(d=function(E){var I=r.find(function(D){if(D=x.get(D))return D.slice(0,
|
||||||
|
E).every(function(na){return na})});if(I)return h=I,"break"},e=q?3:1;0<e&&"break"!==d(e);e--);b.placement!==h&&(b.modifiersData[a]._skip=!0,b.placement=h,b.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}},{name:"preventOverflow",enabled:!0,phase:"main",fn:function(a){var b=a.state,c=a.options;a=a.name;var d=c.mainAxis,e=void 0===d?!0:d;d=c.altAxis;var f=void 0===d?!1:d;d=c.tether;var g=void 0===d?!0:d;d=c.tetherOffset;var l=void 0===d?0:d,m=ta(b,{boundary:c.boundary,rootBoundary:c.rootBoundary,
|
||||||
|
padding:c.padding,altBoundary:c.altBoundary}),k=N(b.placement),p=ja(b.placement),q=!p,n=Ga(k);c="x"===n?"y":"x";d=b.modifiersData.popperOffsets;var r=b.rects.reference,x=b.rects.popper;l="function"===typeof l?l(Object.assign({},b.rects,{placement:b.placement})):l;var h="number"===typeof l?{mainAxis:l,altAxis:l}:Object.assign({mainAxis:0,altAxis:0},l),t=b.modifiersData.offset?b.modifiersData.offset[b.placement]:null;l={x:0,y:0};if(d){if(e){var v,A="y"===n?"top":"left",u="y"===n?"bottom":"right",w=
|
||||||
|
"y"===n?"height":"width";e=d[n];var y=e+m[A],Y=e-m[u],E=g?-x[w]/2:0,I="start"===p?r[w]:x[w];p="start"===p?-x[w]:-r[w];var D=b.elements.arrow;D=g&&D?Fa(D):{width:0,height:0};var na=b.modifiersData["arrow#persistent"]?b.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0};A=na[A];u=na[u];D=L(0,V(r[w],D[w]));I=q?r[w]/2-E-D-A-h.mainAxis:I-D-A-h.mainAxis;q=q?-r[w]/2+E+D+u+h.mainAxis:p+D+u+h.mainAxis;w=(w=b.elements.arrow&&ra(b.elements.arrow))?"y"===n?w.clientTop||0:w.clientLeft||
|
||||||
|
0:0;E=null!=(v=null==t?void 0:t[n])?v:0;v=e+q-E;y=g?V(y,e+I-E-w):y;v=g?L(Y,v):Y;v=L(y,V(e,v));d[n]=v;l[n]=v-e}if(f){var J;f=d[c];e="y"===c?"height":"width";v=f+m["x"===n?"top":"left"];m=f-m["x"===n?"bottom":"right"];k=-1!==["top","left"].indexOf(k);n=null!=(J=null==t?void 0:t[c])?J:0;J=k?v:f-r[e]-x[e]-n+h.altAxis;r=k?f+r[e]+x[e]-n-h.altAxis:m;g&&k?(J=L(J,V(f,r)),J=J>r?r:J):J=L(g?J:v,V(f,g?r:m));d[c]=J;l[c]=J-f}b.modifiersData[a]=l}},requiresIfExists:["offset"]},{name:"arrow",enabled:!0,phase:"main",
|
||||||
|
fn:function(a){var b,c=a.state,d=a.name,e=a.options,f=c.elements.arrow,g=c.modifiersData.popperOffsets,l=N(c.placement);a=Ga(l);l=0<=["left","right"].indexOf(l)?"height":"width";if(f&&g){e=e.padding;e="function"===typeof e?e(Object.assign({},c.rects,{placement:c.placement})):e;e=Xa("number"!==typeof e?e:Ya(e,ua));var m=Fa(f),k="y"===a?"top":"left",p="y"===a?"bottom":"right",q=c.rects.reference[l]+c.rects.reference[a]-g[a]-c.rects.popper[l];g=g[a]-c.rects.reference[a];f=(f=ra(f))?"y"===a?f.clientHeight||
|
||||||
|
0:f.clientWidth||0:0;g=f/2-m[l]/2+(q/2-g/2);l=L(e[k],V(g,f-m[l]-e[p]));c.modifiersData[d]=(b={},b[a]=l,b.centerOffset=l-g,b)}},effect:function(a){var b=a.state;a=a.options.element;a=void 0===a?"[data-popper-arrow]":a;if(null!=a){if("string"===typeof a&&(a=b.elements.popper.querySelector(a),!a))return;Va(b.elements.popper,a)&&(b.elements.arrow=a)}},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]},{name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(a){var b=
|
||||||
|
a.state;a=a.name;var c=b.rects.reference,d=b.rects.popper,e=b.modifiersData.preventOverflow,f=ta(b,{elementContext:"reference"}),g=ta(b,{altBoundary:!0});c=fb(f,c);d=fb(g,d,e);e=gb(c);g=gb(d);b.modifiersData[a]={referenceClippingOffsets:c,popperEscapeOffsets:d,isReferenceHidden:e,hasPopperEscaped:g};b.attributes.popper=Object.assign({},b.attributes.popper,{"data-popper-reference-hidden":e,"data-popper-escaped":g})}}]});let R,va=[],ma=[],Aa=[],ob=[],Yb=Promise.resolve(),Pa=!1,Oa=new Set,Ba=0,Ca=new Set,
|
||||||
|
ba;class T{$destroy(){X(this,1);this.$destroy=G}$on(a,b){let c=this.$$.callbacks[a]||(this.$$.callbacks[a]=[]);c.push(b);return()=>{let d=c.indexOf(b);-1!==d&&c.splice(d,1)}}$set(a){this.$$set&&0!==Object.keys(a).length&&(this.$$.skip_bound=!0,this.$$set(a),this.$$.skip_bound=!1)}}class ac extends T{constructor(a){super();S(this,a,$b,Zb,Q,{config:6,step:7})}}class pc extends T{constructor(a){super();S(this,a,cc,bc,Q,{step:0})}}class ic extends T{constructor(a){super();S(this,a,ec,dc,Q,{cancelIcon:0,
|
||||||
|
step:2})}}class hc extends T{constructor(a){super();S(this,a,gc,fc,Q,{labelId:1,element:0,title:2})}}class nc extends T{constructor(a){super();S(this,a,kc,jc,Q,{labelId:0,step:1})}}class oc extends T{constructor(a){super();S(this,a,mc,lc,Q,{descriptionId:1,element:0,step:2})}}class tc extends T{constructor(a){super();S(this,a,rc,qc,Q,{descriptionId:0,labelId:1,step:2})}}class Bc extends T{constructor(a){super();S(this,a,uc,sc,Q,{classPrefix:11,element:0,descriptionId:2,firstFocusableElement:8,focusableElements:9,
|
||||||
|
labelId:3,lastFocusableElement:10,step:4,dataStepId:1,getElement:12})}get getElement(){return this.$$.ctx[12]}}var Bb=function(a,b){return b={exports:{}},a(b,b.exports),b.exports}(function(a,b){(function(){a.exports={polyfill:function(){function c(h,t){this.scrollLeft=h;this.scrollTop=t}function d(h){if(null===h||"object"!==typeof h||void 0===h.behavior||"auto"===h.behavior||"instant"===h.behavior)return!0;if("object"===typeof h&&"smooth"===h.behavior)return!1;throw new TypeError("behavior member of ScrollOptions "+
|
||||||
|
h.behavior+" is not a valid value for enumeration ScrollBehavior.");}function e(h,t){if("Y"===t)return h.clientHeight+x<h.scrollHeight;if("X"===t)return h.clientWidth+x<h.scrollWidth}function f(h,t){h=k.getComputedStyle(h,null)["overflow"+t];return"auto"===h||"scroll"===h}function g(h){var t=e(h,"Y")&&f(h,"Y");h=e(h,"X")&&f(h,"X");return t||h}function l(h){var t=(r()-h.startTime)/468;var v=.5*(1-Math.cos(Math.PI*(1<t?1:t)));t=h.startX+(h.x-h.startX)*v;v=h.startY+(h.y-h.startY)*v;h.method.call(h.scrollable,
|
||||||
|
t,v);t===h.x&&v===h.y||k.requestAnimationFrame(l.bind(k,h))}function m(h,t,v){var A=r();if(h===p.body){var u=k;var w=k.scrollX||k.pageXOffset;h=k.scrollY||k.pageYOffset;var y=n.scroll}else u=h,w=h.scrollLeft,h=h.scrollTop,y=c;l({scrollable:u,method:y,startTime:A,startX:w,startY:h,x:t,y:v})}var k=window,p=document;if(!("scrollBehavior"in p.documentElement.style&&!0!==k.__forceSmoothScrollPolyfill__)){var q=k.HTMLElement||k.Element,n={scroll:k.scroll||k.scrollTo,scrollBy:k.scrollBy,elementScroll:q.prototype.scroll||
|
||||||
|
c,scrollIntoView:q.prototype.scrollIntoView},r=k.performance&&k.performance.now?k.performance.now.bind(k.performance):Date.now,x=/MSIE |Trident\/|Edge\//.test(k.navigator.userAgent)?1:0;k.scroll=k.scrollTo=function(h,t){void 0!==h&&(!0===d(h)?n.scroll.call(k,void 0!==h.left?h.left:"object"!==typeof h?h:k.scrollX||k.pageXOffset,void 0!==h.top?h.top:void 0!==t?t:k.scrollY||k.pageYOffset):m.call(k,p.body,void 0!==h.left?~~h.left:k.scrollX||k.pageXOffset,void 0!==h.top?~~h.top:k.scrollY||k.pageYOffset))};
|
||||||
|
k.scrollBy=function(h,t){void 0!==h&&(d(h)?n.scrollBy.call(k,void 0!==h.left?h.left:"object"!==typeof h?h:0,void 0!==h.top?h.top:void 0!==t?t:0):m.call(k,p.body,~~h.left+(k.scrollX||k.pageXOffset),~~h.top+(k.scrollY||k.pageYOffset)))};q.prototype.scroll=q.prototype.scrollTo=function(h,t){if(void 0!==h)if(!0===d(h)){if("number"===typeof h&&void 0===t)throw new SyntaxError("Value could not be converted");n.elementScroll.call(this,void 0!==h.left?~~h.left:"object"!==typeof h?~~h:this.scrollLeft,void 0!==
|
||||||
|
h.top?~~h.top:void 0!==t?~~t:this.scrollTop)}else t=h.left,h=h.top,m.call(this,this,"undefined"===typeof t?this.scrollLeft:~~t,"undefined"===typeof h?this.scrollTop:~~h)};q.prototype.scrollBy=function(h,t){void 0!==h&&(!0===d(h)?n.elementScroll.call(this,void 0!==h.left?~~h.left+this.scrollLeft:~~h+this.scrollLeft,void 0!==h.top?~~h.top+this.scrollTop:~~t+this.scrollTop):this.scroll({left:~~h.left+this.scrollLeft,top:~~h.top+this.scrollTop,behavior:h.behavior}))};q.prototype.scrollIntoView=function(h){if(!0===
|
||||||
|
d(h))n.scrollIntoView.call(this,void 0===h?!0:h);else{for(h=this;h!==p.body&&!1===g(h);)h=h.parentNode||h.host;var t=h.getBoundingClientRect(),v=this.getBoundingClientRect();h!==p.body?(m.call(this,h,h.scrollLeft+v.left-t.left,h.scrollTop+v.top-t.top),"fixed"!==k.getComputedStyle(h).position&&k.scrollBy({left:t.left,top:t.top,behavior:"smooth"})):k.scrollBy({left:v.left,top:v.top,behavior:"smooth"})}}}}}})()});Bb.polyfill;Bb.polyfill();class Ra extends Qa{constructor(a,b){void 0===b&&(b={});super(a,
|
||||||
|
b);this.tour=a;this.classPrefix=this.tour.options?ib(this.tour.options.classPrefix):"";this.styles=a.styles;this._resolvedAttachTo=null;Ua(this);this._setOptions(b);return this}cancel(){this.tour.cancel();this.trigger("cancel")}complete(){this.tour.complete();this.trigger("complete")}destroy(){this.tooltip&&(this.tooltip.destroy(),this.tooltip=null);this.el instanceof HTMLElement&&this.el.parentNode&&(this.el.parentNode.removeChild(this.el),this.el=null);this._updateStepTargetOnHide();this.trigger("destroy")}getTour(){return this.tour}hide(){this.tour.modal.hide();
|
||||||
|
this.trigger("before-hide");this.el&&(this.el.hidden=!0);this._updateStepTargetOnHide();this.trigger("hide")}_resolveAttachToOptions(){let a=this.options.attachTo||{},b=Object.assign({},a);Z(b.element)&&(b.element=b.element.call(this));if(qa(b.element)){try{b.element=document.querySelector(b.element)}catch(c){}b.element||console.error(`The element for this Shepherd step was not found ${a.element}`)}return this._resolvedAttachTo=b}_getResolvedAttachToOptions(){return null===this._resolvedAttachTo?
|
||||||
|
this._resolveAttachToOptions():this._resolvedAttachTo}isOpen(){return!(!this.el||this.el.hidden)}show(){if(Z(this.options.beforeShowPromise)){let a=this.options.beforeShowPromise();if(void 0!==a)return a.then(()=>this._show())}this._show()}updateStepOptions(a){Object.assign(this.options,a);this.shepherdElementComponent&&this.shepherdElementComponent.$set({step:this})}getElement(){return this.el}getTarget(){return this.target}_createTooltipContent(){this.shepherdElementComponent=new Bc({target:this.tour.options.stepsContainer||
|
||||||
|
document.body,props:{classPrefix:this.classPrefix,descriptionId:`${this.id}-description`,labelId:`${this.id}-label`,step:this,styles:this.styles}});return this.shepherdElementComponent.getElement()}_scrollTo(a){let {element:b}=this._getResolvedAttachToOptions();Z(this.options.scrollToHandler)?this.options.scrollToHandler(b):b instanceof Element&&"function"===typeof b.scrollIntoView&&b.scrollIntoView(a)}_getClassOptions(a){var b=this.tour&&this.tour.options&&this.tour.options.defaultStepOptions;b=
|
||||||
|
b&&b.classes?b.classes:"";a=[...(a.classes?a.classes:"").split(" "),...b.split(" ")];a=new Set(a);return Array.from(a).join(" ").trim()}_setOptions(a){void 0===a&&(a={});let b=this.tour&&this.tour.options&&this.tour.options.defaultStepOptions;b=zc({},b||{});this.options=Object.assign({arrow:!0},b,a);let {when:c}=this.options;this.options.classes=this._getClassOptions(a);this.destroy();this.id=this.options.id||`step-${Ma()}`;c&&Object.keys(c).forEach(d=>{this.on(d,c[d],this)})}_setupElements(){void 0!==
|
||||||
|
this.el&&this.destroy();this.el=this._createTooltipContent();this.options.advanceOn&&Gb(this);this.tooltip&&this.tooltip.destroy();let a=this._getResolvedAttachToOptions(),b=a.element,c=Wb(a,this);void 0!==a&&null!==a&&a.element&&a.on||(b=document.body,this.shepherdElementComponent.getElement().classList.add("shepherd-centered"));this.tooltip=Ac(b,this.el,c);this.target=a.element}_show(){this.trigger("before-show");this._resolveAttachToOptions();this._setupElements();this.tour.modal||this.tour._setupModal();
|
||||||
|
this.tour.modal.setupForStep(this);this._styleTargetElementForStep(this);this.el.hidden=!1;this.options.scrollTo&&setTimeout(()=>{this._scrollTo(this.options.scrollTo)});this.el.hidden=!1;let a=this.shepherdElementComponent.getElement(),b=this.target||document.body;b.classList.add(`${this.classPrefix}shepherd-enabled`);b.classList.add(`${this.classPrefix}shepherd-target`);a.classList.add("shepherd-enabled");this.trigger("show")}_styleTargetElementForStep(a){let b=a.target;b&&(a.options.highlightClass&&
|
||||||
|
b.classList.add(a.options.highlightClass),b.classList.remove("shepherd-target-click-disabled"),!1===a.options.canClickTarget&&b.classList.add("shepherd-target-click-disabled"))}_updateStepTargetOnHide(){let a=this.target||document.body;this.options.highlightClass&&a.classList.remove(this.options.highlightClass);a.classList.remove("shepherd-target-click-disabled",`${this.classPrefix}shepherd-enabled`,`${this.classPrefix}shepherd-target`)}}class Cc extends T{constructor(a){super();S(this,a,xc,wc,Q,
|
||||||
|
{element:0,openingProperties:4,getElement:5,closeModalOpening:6,hide:7,positionModal:8,setupForStep:9,show:10})}get getElement(){return this.$$.ctx[5]}get closeModalOpening(){return this.$$.ctx[6]}get hide(){return this.$$.ctx[7]}get positionModal(){return this.$$.ctx[8]}get setupForStep(){return this.$$.ctx[9]}get show(){return this.$$.ctx[10]}}let oa=new Qa;class Dc extends Qa{constructor(a){void 0===a&&(a={});super(a);Ua(this);this.options=Object.assign({},{exitOnEsc:!0,keyboardNavigation:!0},
|
||||||
|
a);this.classPrefix=ib(this.options.classPrefix);this.steps=[];this.addSteps(this.options.steps);"active cancel complete inactive show start".split(" ").map(b=>{(c=>{this.on(c,d=>{d=d||{};d.tour=this;oa.trigger(c,d)})})(b)});this._setTourID();return this}addStep(a,b){a instanceof Ra?a.tour=this:a=new Ra(this,a);void 0!==b?this.steps.splice(b,0,a):this.steps.push(a);return a}addSteps(a){Array.isArray(a)&&a.forEach(b=>{this.addStep(b)});return this}back(){let a=this.steps.indexOf(this.currentStep);
|
||||||
|
this.show(a-1,!1)}cancel(){this.options.confirmCancel?window.confirm(this.options.confirmCancelMessage||"Are you sure you want to stop the tour?")&&this._done("cancel"):this._done("cancel")}complete(){this._done("complete")}getById(a){return this.steps.find(b=>b.id===a)}getCurrentStep(){return this.currentStep}hide(){let a=this.getCurrentStep();if(a)return a.hide()}isActive(){return oa.activeTour===this}next(){let a=this.steps.indexOf(this.currentStep);a===this.steps.length-1?this.complete():this.show(a+
|
||||||
|
1,!0)}removeStep(a){let b=this.getCurrentStep();this.steps.some((c,d)=>{if(c.id===a)return c.isOpen()&&c.hide(),c.destroy(),this.steps.splice(d,1),!0});b&&b.id===a&&(this.currentStep=void 0,this.steps.length?this.show(0):this.cancel())}show(a,b){void 0===a&&(a=0);void 0===b&&(b=!0);if(a=qa(a)?this.getById(a):this.steps[a])this._updateStateBeforeShow(),Z(a.options.showOn)&&!a.options.showOn()?this._skipStep(a,b):(this.trigger("show",{step:a,previous:this.currentStep}),this.currentStep=a,a.show())}start(){this.trigger("start");
|
||||||
|
this.focusedElBeforeOpen=document.activeElement;this.currentStep=null;this._setupModal();this._setupActiveTour();this.next()}_done(a){let b=this.steps.indexOf(this.currentStep);Array.isArray(this.steps)&&this.steps.forEach(c=>c.destroy());vc(this);this.trigger(a,{index:b});oa.activeTour=null;this.trigger("inactive",{tour:this});this.modal&&this.modal.hide();"cancel"!==a&&"complete"!==a||!this.modal||(a=document.querySelector(".shepherd-modal-overlay-container"))&&a.remove();this.focusedElBeforeOpen instanceof
|
||||||
|
HTMLElement&&this.focusedElBeforeOpen.focus()}_setupActiveTour(){this.trigger("active",{tour:this});oa.activeTour=this}_setupModal(){this.modal=new Cc({target:this.options.modalContainer||document.body,props:{classPrefix:this.classPrefix,styles:this.styles}})}_skipStep(a,b){a=this.steps.indexOf(a);a===this.steps.length-1?this.complete():this.show(b?a+1:a-1,b)}_updateStateBeforeShow(){this.currentStep&&this.currentStep.hide();this.isActive()||this._setupActiveTour()}_setTourID(){this.id=`${this.options.tourName||
|
||||||
|
"tour"}--${Ma()}`}}Object.assign(oa,{Tour:Dc,Step:Ra});return oa})
|
||||||
|
//# sourceMappingURL=shepherd.min.js.map
|
|
@ -113,7 +113,7 @@
|
||||||
|
|
||||||
{% include 'snippets/rate_action.html' with user=request.user book=book %}
|
{% include 'snippets/rate_action.html' with user=request.user book=book %}
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3" id="tour-shelve-button">
|
||||||
{% include 'snippets/shelve_button/shelve_button.html' %}
|
{% include 'snippets/shelve_button/shelve_button.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@
|
||||||
|
|
||||||
{% with work=book.parent_work %}
|
{% with work=book.parent_work %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ work.local_path }}/editions">
|
<a href="{{ work.local_path }}/editions" id="tour-other-editions-link">
|
||||||
{% blocktrans trimmed count counter=work.editions.count with count=work.editions.count|intcomma %}
|
{% blocktrans trimmed count counter=work.editions.count with count=work.editions.count|intcomma %}
|
||||||
{{ count }} edition
|
{{ count }} edition
|
||||||
{% plural %}
|
{% plural %}
|
||||||
|
@ -254,7 +254,7 @@
|
||||||
<h2 class="title is-5">{% trans "Your reading activity" %}</h2>
|
<h2 class="title is-5">{% trans "Your reading activity" %}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<button class="button is-small" data-modal-open="add-readthrough">
|
<button class="button is-small" data-modal-open="add-readthrough" id="tour-add-readthrough">
|
||||||
<span class="icon icon-plus m-mobile-0" aria-hidden="true"></span>
|
<span class="icon icon-plus m-mobile-0" aria-hidden="true"></span>
|
||||||
<span class="is-sr-only-mobile">
|
<span class="is-sr-only-mobile">
|
||||||
{% trans "Add read dates" %}
|
{% trans "Add read dates" %}
|
||||||
|
@ -392,7 +392,7 @@
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<section class="content block">
|
<section class="content block" id="tour-book-file-links">
|
||||||
{% include "book/file_links/links.html" %}
|
{% include "book/file_links/links.html" %}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -405,4 +405,7 @@
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{% static "js/tabs.js" %}?v={{ js_cache }}"></script>
|
<script src="{% static "js/tabs.js" %}?v={{ js_cache }}"></script>
|
||||||
<script src="{% static "js/autocomplete.js" %}?v={{ js_cache }}"></script>
|
<script src="{% static "js/autocomplete.js" %}?v={{ js_cache }}"></script>
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/book.html' %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -19,16 +19,8 @@
|
||||||
name="email"
|
name="email"
|
||||||
class="input"
|
class="input"
|
||||||
id="email"
|
id="email"
|
||||||
aria-described-by="id_email_errors"
|
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
{% if error %}
|
|
||||||
<div id="id_email_errors">
|
|
||||||
<p class="help is-danger">
|
|
||||||
{% trans "No user matching this email address found." %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
{% include 'snippets/create_status/status.html' with type="direct" uuid=1 mention=partner no_script=True %}
|
{% include 'snippets/create_status/status.html' with type="direct" uuid=1 mention=partner %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="block">
|
<section class="block">
|
||||||
|
@ -30,3 +30,4 @@
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends 'feed/layout.html' %}
|
{% extends 'feed/layout.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
{% block panel %}
|
{% block panel %}
|
||||||
|
|
||||||
|
@ -73,3 +74,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script src="{% static "js/tabs.js" %}?v={{ js_cache }}"></script>
|
||||||
|
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/home.html' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block title %}{% trans "Updates" %}{% endblock %}
|
{% block title %}{% trans "Updates" %}{% endblock %}
|
||||||
|
|
||||||
|
@ -30,6 +29,4 @@
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script src="{% static "js/tabs.js" %}?v={{ js_cache }}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{% load feed_page_tags %}
|
{% load feed_page_tags %}
|
||||||
|
|
||||||
{% suggested_books as suggested_books %}
|
{% suggested_books as suggested_books %}
|
||||||
<section class="block">
|
<section id="tour-suggested-books" class="block">
|
||||||
<h2 class="title is-4">{% trans "Your Books" %}</h2>
|
<h2 class="title is-4">{% trans "Your Books" %}</h2>
|
||||||
{% if not suggested_books %}
|
{% if not suggested_books %}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="column is-two-thirds">
|
<div class="column is-two-thirds">
|
||||||
<input type="hidden" name="user" value="{{ request.user.id }}" />
|
<input type="hidden" name="user" value="{{ request.user.id }}" />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="group_form_id_name">{% trans "Group Name:" %}</label>
|
<label class="label" for="group_form_id_name" id="tour-group-name">{% trans "Group Name:" %}</label>
|
||||||
{{ group_form.name }}
|
{{ group_form.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% if request.user.is_authenticated and group|is_member:request.user %}
|
{% if request.user.is_authenticated and group|is_member:request.user %}
|
||||||
<div class="column is-narrow is-flex">
|
<div class="column is-narrow is-flex" id="tour-create-list">
|
||||||
{% trans "Create List" as button_text %}
|
{% trans "Create List" as button_text %}
|
||||||
{% include 'snippets/toggle/open_button.html' with controls_text="create_list" icon_with_text="plus" text=button_text focus="create_list_header" %}
|
{% include 'snippets/toggle/open_button.html' with controls_text="create_list" icon_with_text="plus" text=button_text focus="create_list_header" %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,3 +80,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/group.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input type="text" name="user_query" value="{{ request.GET.user_query }}" class="input" placeholder="{% trans 'Search to add a user' %}" aria-label="{% trans 'Search to add a user' %}">
|
<input type="text" name="user_query" value="{{ request.GET.user_query }}" class="input" placeholder="{% trans 'Search to add a user' %}" aria-label="{% trans 'Search to add a user' %}">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control" id="tour-group-member-search">
|
||||||
<button class="button" type="submit">
|
<button class="button" type="submit">
|
||||||
<span class="icon icon-search" title="{% trans 'Search' %}">
|
<span class="icon icon-search" title="{% trans 'Search' %}">
|
||||||
<span class="is-sr-only">{% trans "Search" %}</span>
|
<span class="is-sr-only">{% trans "Search" %}</span>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
<span title="@{{ member|username }}" class="is-block pb-3">@{{ member|username|truncatechars:8 }}</span>
|
<span title="@{{ member|username }}" class="is-block pb-3">@{{ member|username|truncatechars:8 }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% if group.user == member %}
|
{% if group.user == member %}
|
||||||
<span class="icon icon-star-full" title="Manager">
|
<span class="icon icon-star-full" title="Manager" id="tour-group-owner">
|
||||||
<span class="is-sr-only">Manager</span>
|
<span class="is-sr-only">Manager</span>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
303
bookwyrm/templates/guided_tour/book.html
Normal file
303
bookwyrm/templates/guided_tour/book.html
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const tour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
tour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'This is home page of a book. Let\'s see what you can do while you\'re here!' %}",
|
||||||
|
title: "{% trans 'Book page' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'This is where you can set a reading status for this book. You can press the button to move to the next stage, or use the drop down button to select the reading status you want to set.' %}",
|
||||||
|
title: "{% trans 'Reading status' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-shelve-button",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'You can also manually add reading dates here. Unlike changing the reading status using the previous method, adding dates manually will not automatically add them to your <strong>Read</strong> or <strong>Reading</strong> shelves.<br><br>Got a favourite you re-read every year? We\'ve got you covered - you can add multiple read dates for the same book 😀' %}",
|
||||||
|
title: "{% trans 'Add read dates' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-add-readthrough",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'There can be multiple editions of a book, in various formats or languages. You can choose which edition you want to use.' %}",
|
||||||
|
title: "{% trans 'Other editions' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-other-editions-link",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'You can post a review, comment, or quote here.' %}",
|
||||||
|
title: "{% trans 'Share your thoughts' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: ".tour-review-comment-quote",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'If you have read this book you can post a review including an optional star rating' %}",
|
||||||
|
title: "{% trans 'Post a review' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "[id^=tab_review]",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'You can share your thoughts on this book generally with a simple comment' %}",
|
||||||
|
title: "{% trans 'Post a comment' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "[id^=tab_comment]",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Just read some perfect prose? Let the world know by sharing a quote!' %}",
|
||||||
|
title: "{% trans 'Share a quote' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "[id^=tab_quote]",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'If your review or comment might ruin the book for someone who hasn\'t read it yet, you can hide your post behind a <strong>spoiler alert</strong>' %}",
|
||||||
|
title: "{% trans 'Spoiler alerts' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "",
|
||||||
|
element: "[id^=form_review] > .tour-spoiler-alert",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Choose who can see your post here. Post privacy can be <strong>Public</strong> (everyone can see), <strong>Unlisted</strong> (everyone can see, but it doesn\'t appear in public feeds or discovery pages), <strong>Followers</strong> (only your followers can see), or <strong>Private</strong> (only you can see)' %}",
|
||||||
|
title: "{% trans 'Post privacy' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "[id^=form_review] [id^=privacy_]",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Some ebooks can be downloaded for free from external sources. They will be shown here.' %}",
|
||||||
|
title: "{% trans 'Download links' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-book-file-links",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans '<p class=\'notification is-warning is-light mt-3\'> Continue the tour by selecting <strong>Your books</strong> from the drop down menu.</p>' %}",
|
||||||
|
title: "{% trans 'Next' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: () => {
|
||||||
|
let menu = document.querySelector('#navbar-dropdown')
|
||||||
|
let display = window.getComputedStyle(menu).display;
|
||||||
|
return display == 'flex' ? '#navbar-dropdown' : '.navbar-burger';
|
||||||
|
},
|
||||||
|
on: "left-end",
|
||||||
|
},
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Ok' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
tour.start()
|
||||||
|
</script>
|
122
bookwyrm/templates/guided_tour/group.html
Normal file
122
bookwyrm/templates/guided_tour/group.html
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const tour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
tour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'Welcome to the page for your group! This is where you can add and remove users, create user-curated lists, and edit the group details.' %}",
|
||||||
|
title: "{% trans 'Your group' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger guided-tour-cancel-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Use this search box to find users to join your group. Currently users must be members of the same Bookwyrm instance and be invited by the group owner.' %}",
|
||||||
|
title: "{% trans 'Find users' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-group-member-search",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Your group members will appear here. The group owner is marked with a star symbol.' %}",
|
||||||
|
title: "{% trans 'Group members' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-group-owner",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'As well as creating lists from the Lists page, you can create a group-curated list here on the group\'s homepage. Any member of the group can create a list curated by group members.' %}",
|
||||||
|
title: "{% trans 'Group lists' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-create-list",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Congratulations, you\'ve finished the tour! Now you know the basics, but there is lots more to explore on your own. Happy reading!' %}",
|
||||||
|
title: "{% trans 'Finish' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'End tour' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
tour.start()
|
||||||
|
</script>
|
225
bookwyrm/templates/guided_tour/home.html
Normal file
225
bookwyrm/templates/guided_tour/home.html
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const initiateTour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkResponsiveState(anchor) {
|
||||||
|
let menu = document.querySelector('#navbar-dropdown');
|
||||||
|
let display = window.getComputedStyle(menu).display;
|
||||||
|
return display == 'flex' ? anchor : '.navbar-burger';
|
||||||
|
}
|
||||||
|
|
||||||
|
initiateTour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'Welcome to Bookwyrm!<br><br>Would you like to take the guided tour to help you get started?' %}",
|
||||||
|
title: "{% trans 'Guided Tour' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'No thanks' %}",
|
||||||
|
classes: "is-danger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
this.cancel();
|
||||||
|
return homeTour.start()
|
||||||
|
},
|
||||||
|
text: "{% trans 'Yes please!' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'If you ever change your mind, just click on the Guided Tour link to start your tour' %}",
|
||||||
|
title: "{% trans 'Guided Tour' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-begin",
|
||||||
|
on: "left-start",
|
||||||
|
},
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.complete()
|
||||||
|
},
|
||||||
|
text: "{% trans 'Ok' %}",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const homeTour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
homeTour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'Search for books, users, or lists using this search box.' %}",
|
||||||
|
title: "{% trans 'Search box' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-search",
|
||||||
|
on: "bottom",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Search book records by scanning an ISBN barcode using your device\'s camera - great when you\'re in the bookstore or library!' %}",
|
||||||
|
title: "{% trans 'Barcode reader' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-barcode",
|
||||||
|
on: "bottom",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Use the <strong>Feed</strong>, <strong>Lists</strong> and <strong>Discover</strong> links to discover the latest news from your feed, lists of books by topic, and the latest happenings on this Bookwyrm server!' %}",
|
||||||
|
title: "{% trans 'Navigation Bar' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: checkResponsiveState('#tour-navbar-start'),
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Books on your reading status shelves will be shown here.' %}",
|
||||||
|
title: "{% trans 'Your Books' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-suggested-books",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Updates from people you are following will appear in your <strong>Home</strong> timeline.<br><br>The <strong>Books</strong> tab shows activity from anyone, related to your books.' %}",
|
||||||
|
title: "{% trans 'Timelines' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#feed",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'The bell will light up when you have a new notification. When it does, click on it to find out what exciting thing has happened!' %}",
|
||||||
|
title: "{% trans 'Notifications' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: checkResponsiveState('#tour-notifications'),
|
||||||
|
on: "left-end",
|
||||||
|
},
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Your profile, books, direct messages, and settings can be accessed by clicking on your name in the menu here.<p class=\'notification is-warning is-light mt-3\'>Try selecting <strong>Profile</strong> from the drop down menu to continue the tour.</p>' %}",
|
||||||
|
title: "{% trans 'Profile and settings menu' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: checkResponsiveState('#navbar-dropdown'),
|
||||||
|
on: "left-end",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Ok' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
initiateTour.start()
|
||||||
|
</script>
|
150
bookwyrm/templates/guided_tour/lists.html
Normal file
150
bookwyrm/templates/guided_tour/lists.html
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
{% load i18n %}
|
||||||
|
{% load utilities %}
|
||||||
|
{% load user_page_tags %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const tour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
tour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'This is the lists page where you can discover book lists created by any user. A List is a collection of books, similar to a shelf.<br><br>Shelves are for organising books for yourself, whereas Lists are generally for sharing with others.' %}",
|
||||||
|
title: "{% trans 'Lists' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Let\'s see how to create a new list.<p class=\'notification is-warning is-light mt-3\'>Click the <strong>Create List</strong> button, then <strong>Next</strong> to continue the tour</p>' %}",
|
||||||
|
title: "{% trans 'Creating a new list' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-create-list",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'You must give your list a name and can optionally give it a description to help other people understand what your list is about.' %}",
|
||||||
|
title: "{% trans 'Creating a new list' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-list-name",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Choose who can see your list here. List privacy options work just like we saw when posting book reviews. This is a common pattern throughout Bookwyrm.' %}",
|
||||||
|
title: "{% trans 'List privacy' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-privacy-select",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'You can also decide how your list is to be curated - only by you, by anyone, or by a group.' %}",
|
||||||
|
title: "{% trans 'List curation' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-list-curation",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Next in our tour we will explore Groups!' %}",
|
||||||
|
title: "{% trans 'Next: Groups' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
this.complete();
|
||||||
|
window.location = "{% url 'user-groups' user|username %}"
|
||||||
|
},
|
||||||
|
text: "{% trans 'Take me there' %}"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
tour.start()
|
||||||
|
</script>
|
167
bookwyrm/templates/guided_tour/search.html
Normal file
167
bookwyrm/templates/guided_tour/search.html
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
let localResult = document.querySelector(".local-book-search-result");
|
||||||
|
let remoteResult = document.querySelector(".remote-book-search-result");
|
||||||
|
let otherCatalogues = document.querySelector("#tour-load-from-other-catalogues");
|
||||||
|
let manuallyAdd = document.querySelector("#tour-manually-add-book");
|
||||||
|
const tour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (remoteResult) {
|
||||||
|
tour.addStep(
|
||||||
|
{
|
||||||
|
text: "{% trans 'If the book you are looking for is available on a remote catalogue such as Open Library, click on <strong>Import book</strong>.' %}",
|
||||||
|
title: "{% trans 'Searching' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-remote-search-result",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger guided-tour-cancel-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} else if (localResult) {
|
||||||
|
tour.addStep(
|
||||||
|
{
|
||||||
|
text: "{% trans 'If the book you are looking for is already on this Bookwyrm instance, you can click on the title to go to the book\'s page.' %}",
|
||||||
|
title: "{% trans 'Searching' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-local-book-search-result",
|
||||||
|
on: "top",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger guided-tour-cancel-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherCatalogues) {
|
||||||
|
tour.addStep({
|
||||||
|
text: "{% trans 'If the book you are looking for is not listed, try loading more records from other sources like Open Library or Inventaire.' %}",
|
||||||
|
title: "{% trans 'Load more records' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-load-from-other-catalogues",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manuallyAdd) {
|
||||||
|
tour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'If your book is not in the results, try adjusting your search terms.' %}",
|
||||||
|
title: "{% trans 'Search again' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: '#tour-search-page-input',
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'If you still can\'t find your book, you can add a record manually.' %}",
|
||||||
|
title: "{% trans 'Add a record manally' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-manually-add-book",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
|
tour.addStep({
|
||||||
|
text: "{% trans '<p class=\'notification is-warning is-light mt-3\'>Import, manually add, or view an existing book to continue the tour.<p>' %}",
|
||||||
|
title: "{% trans 'Continue the tour' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Ok' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
tour.start()
|
||||||
|
</script>
|
131
bookwyrm/templates/guided_tour/user_books.html
Normal file
131
bookwyrm/templates/guided_tour/user_books.html
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const tour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
tour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'This is the page where your books are listed, organised into shelves.' %}",
|
||||||
|
title: "{% trans 'Your books' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans '<strong>To Read</strong>, <strong>Currently Reading</strong>, <strong>Read</strong>, and <strong>Stopped Reading</strong> are default shelves. When you change the reading status of a book it will automatically be moved to the matching shelf. A book can only be on one default shelf at a time.' %}",
|
||||||
|
title: "{% trans 'Reading status shelves' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-user-shelves",
|
||||||
|
on: "bottom-start",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'You can create additional custom shelves to organise your books. A book on a custom shelf can be on any number of other shelves simultaneously, including one of the default reading status shelves' %}",
|
||||||
|
title: "{% trans 'Adding custom shelves.' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-create-shelf",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'If you have an export file from another service like Goodreads or LibraryThing, you can import it here.' %}",
|
||||||
|
title: "{% trans 'Import from another service' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-import-books",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Now that we\'ve explored book shelves, let\'s take a look at a related concept: book lists!<p class=\'notification is-warning is-light mt-3\'>Click on the <strong>Lists</strong> link here to continue the tour.' %}",
|
||||||
|
title: "{% trans 'Lists' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: () => {
|
||||||
|
let menu = document.querySelector('#tour-navbar-start')
|
||||||
|
let display = window.getComputedStyle(menu).display;
|
||||||
|
return display == 'flex' ? '#tour-navbar-start' : '.navbar-burger';
|
||||||
|
},
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
this.complete();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Ok' %}"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
tour.start()
|
||||||
|
</script>
|
123
bookwyrm/templates/guided_tour/user_groups.html
Normal file
123
bookwyrm/templates/guided_tour/user_groups.html
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const tour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
tour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'You can create or join a group with other users. Groups can share group-curated book lists, and in future will be able to do other things.' %}",
|
||||||
|
title: "{% trans 'Groups' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Let\'s create a new group!<p class=\'notification is-warning is-light mt-3\'>Click the <strong>Create group</strong> button, then <strong>Next</strong> to continue the tour</p>' %}",
|
||||||
|
title: "{% trans 'Create group' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-create-group",
|
||||||
|
on: "left-start",
|
||||||
|
},
|
||||||
|
highlightClass: 'tour-element-highlight',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Give your group a name and describe what it is about. You can make user groups for any purpose - a reading group, a bunch of friends, whatever!' %}",
|
||||||
|
title: "{% trans 'Creating a group' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-group-name",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Groups have privacy settings just like posts and lists, except that group privacy cannot be <strong>Followers</strong>.' %}",
|
||||||
|
title: "{% trans 'Group visibility' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-privacy",
|
||||||
|
on: "left",
|
||||||
|
},
|
||||||
|
scrollTo: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Once you\'re happy with how everything is set up, click the <strong>Save</strong> button to create your new group.<p class=\'notification is-warning is-light mt-3\'>Create and save a group to continue the tour.</p>' %}",
|
||||||
|
title: "{% trans 'Save your group' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Ok' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
tour.start()
|
||||||
|
</script>
|
148
bookwyrm/templates/guided_tour/user_profile.html
Normal file
148
bookwyrm/templates/guided_tour/user_profile.html
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const tour = new Shepherd.Tour({
|
||||||
|
exitOnEsc: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
tour.addSteps([
|
||||||
|
{
|
||||||
|
text: "{% trans 'This is your user profile. All your latest activities will be listed here. Other Bookwyrm users can see parts of this page too - what they can see depends on your privacy settings.' %}",
|
||||||
|
title: "{% trans 'User Profile' %}",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
disableGuidedTour(csrf_token);
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'End Tour' %}",
|
||||||
|
classes: "is-danger",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'This tab shows everything you have read towards your annual reading goal, or allows you to set one. You don\'t have to set a reading goal if that\'s not your thing!' %}",
|
||||||
|
title: "{% trans 'Reading Goal' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-reading-goal",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Here you can see your groups, or create a new one. A group brings together Bookwyrm users and allows them to curate lists together.' %}",
|
||||||
|
title: "{% trans 'Groups' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-groups-tab",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'You can see your lists, or create a new one, here. A list is a collection of books that have something in common.' %}",
|
||||||
|
title: "{% trans 'Lists' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-lists-tab",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'The Books tab shows your book shelves. We\'ll explore this later in the tour.' %}",
|
||||||
|
title: "{% trans 'Books' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-shelves-tab",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.next();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Next' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "{% trans 'Now you understand the basics of your profile page, let\s add a book to your shelves.<p class=\'notification is-warning is-light mt-3\'>Search for a title or author to continue the tour.</p>' %}",
|
||||||
|
title: "{% trans 'Find a book' %}",
|
||||||
|
attachTo: {
|
||||||
|
element: "#tour-search",
|
||||||
|
on: "right",
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.back();
|
||||||
|
},
|
||||||
|
secondary: true,
|
||||||
|
text: "{% trans 'Back' %}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action() {
|
||||||
|
return this.complete();
|
||||||
|
},
|
||||||
|
text: "{% trans 'Ok' %}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
tour.start()
|
||||||
|
</script>
|
|
@ -26,7 +26,16 @@
|
||||||
{% trans "Password:" %}
|
{% trans "Password:" %}
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input type="password" name="password" maxlength="128" class="input" required="" id="id_new_password" aria-describedby="form_errors">
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
maxlength="128"
|
||||||
|
class="input"
|
||||||
|
required=""
|
||||||
|
id="id_new_password"
|
||||||
|
aria-describedby="desc_password"
|
||||||
|
>
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.password.errors id="desc_password" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -34,7 +43,8 @@
|
||||||
{% trans "Confirm password:" %}
|
{% trans "Confirm password:" %}
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password" aria-describedby="form_errors">
|
{{ form.confirm_password }}
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.confirm_password.errors id="desc_confirm_password" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans "Search for a book" as search_placeholder %}
|
{% trans "Search for a book" as search_placeholder %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<input aria-label="{{ search_placeholder }}" id="search_input" class="input" type="text" name="q" placeholder="{{ search_placeholder }}" value="{{ query }}">
|
<input aria-label="{{ search_placeholder }}" id="tour-search" class="input" type="text" name="q" placeholder="{{ search_placeholder }}" value="{{ query }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button" type="submit">
|
<button class="button" type="submit">
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button" type="button" data-modal-open="barcode-scanner-modal">
|
<button class="button" type="button" data-modal-open="barcode-scanner-modal">
|
||||||
<span class="icon icon-barcode" title="{% trans 'Scan Barcode' %}">
|
<span class="icon icon-barcode" title="{% trans 'Scan Barcode' %}" id="tour-barcode">
|
||||||
<span class="is-sr-only">{% trans "Scan Barcode" %}</span>
|
<span class="is-sr-only">{% trans "Scan Barcode" %}</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-menu" id="main_nav">
|
<div class="navbar-menu" id="main_nav">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start" id="tour-navbar-start">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<a href="{% url 'lists' %}" class="navbar-item mt-3 py-0">
|
<a href="{% url 'lists' %}" class="navbar-item mt-3 py-0">
|
||||||
{% trans "Lists" %}
|
{% trans "Lists" %}
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
{% include 'user_menu.html' %}
|
{% include 'user_menu.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-item mt-3 py-0">
|
<div class="navbar-item mt-3 py-0">
|
||||||
<a href="{% url 'notifications' %}" class="tags has-addons">
|
<a href="{% url 'notifications' %}" class="tags has-addons" id="tour-notifications">
|
||||||
<span class="tag is-medium">
|
<span class="tag is-medium">
|
||||||
<span class="icon icon-bell" title="{% trans 'Notifications' %}">
|
<span class="icon icon-bell" title="{% trans 'Notifications' %}">
|
||||||
<span class="is-sr-only">{% trans "Notifications" %}</span>
|
<span class="is-sr-only">{% trans "Notifications" %}</span>
|
||||||
|
@ -189,6 +189,12 @@
|
||||||
<p>
|
<p>
|
||||||
<a href="https://docs.joinbookwyrm.com/">{% trans "Documentation" %}</a>
|
<a href="https://docs.joinbookwyrm.com/">{% trans "Documentation" %}</a>
|
||||||
</p>
|
</p>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<p id="tour-begin">
|
||||||
|
<a href="/guided-tour/True">{% trans "Guided Tour" %}</a>
|
||||||
|
<noscript>(requires JavaScript)</noscript>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="column content is-two-fifth">
|
<div class="column content is-two-fifth">
|
||||||
{% if site.support_link %}
|
{% if site.support_link %}
|
||||||
|
@ -219,6 +225,8 @@
|
||||||
<script src="{% static "js/localstorage.js" %}?v={{ js_cache }}"></script>
|
<script src="{% static "js/localstorage.js" %}?v={{ js_cache }}"></script>
|
||||||
<script src="{% static "js/status_cache.js" %}?v={{ js_cache }}"></script>
|
<script src="{% static "js/status_cache.js" %}?v={{ js_cache }}"></script>
|
||||||
<script src="{% static "js/vendor/quagga.min.js" %}?v={{ js_cache }}"></script>
|
<script src="{% static "js/vendor/quagga.min.js" %}?v={{ js_cache }}"></script>
|
||||||
|
<script src="{% static "js/vendor/shepherd.min.js" %}?v={{ js_cache }}"></script>
|
||||||
|
<script src="{% static "js/guided_tour.js" %}?v={{ js_cache }}"></script>
|
||||||
|
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-two-thirds">
|
<div class="column is-two-thirds">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="id_name">{% trans "Name:" %}</label>
|
<label class="label" for="id_name" id="tour-list-name">{% trans "Name:" %}</label>
|
||||||
{{ list_form.name }}
|
{{ list_form.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<fieldset class="field">
|
<fieldset class="field">
|
||||||
<legend class="label">{% trans "List curation:" %}</legend>
|
<legend class="label" id="tour-list-curation">{% trans "List curation:" %}</legend>
|
||||||
|
|
||||||
<div class="field" data-hides="list_group_selector">
|
<div class="field" data-hides="list_group_selector">
|
||||||
<input
|
<input
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
{% with user|username as username %}
|
{% with user|username as username %}
|
||||||
{% url 'user-groups' user|username as url %}
|
{% url 'user-groups' user|username as url %}
|
||||||
<div>
|
<div>
|
||||||
<p>{% trans "You don't have any Groups yet!" %}</p>
|
<p id="tour-no-groups-yet">{% trans "You don't have any Groups yet!" %}</p>
|
||||||
<p>
|
<p>
|
||||||
<a class="help has-text-weight-normal" href="{{ url }}">{% trans "Create a Group" %}</a>
|
<a class="help has-text-weight-normal" href="{{ url }}">{% trans "Create a Group" %}</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<div class="control" id="tour-privacy-select">
|
||||||
{% include 'snippets/privacy_select.html' with current=list.privacy %}
|
{% include 'snippets/privacy_select.html' with current=list.privacy %}
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow" id="tour-create-list">
|
||||||
{% trans "Create List" as button_text %}
|
{% trans "Create List" as button_text %}
|
||||||
{% include 'snippets/toggle/open_button.html' with controls_text="create_list" icon_with_text="plus" text=button_text focus="create_list_header" %}
|
{% include 'snippets/toggle/open_button.html' with controls_text="create_list" icon_with_text="plus" text=button_text focus="create_list_header" %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,3 +54,9 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/lists.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-muted{% endif %}">
|
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-muted{% endif %}">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-clipped">
|
<div class="column is-clipped">
|
||||||
{% include 'snippets/status_preview.html' with status=related_status %}
|
{% include 'notifications/items/status_preview.html' with status=related_status %}
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow has-text-muted">
|
<div class="column is-narrow has-text-muted">
|
||||||
{{ related_status.published_date|timesince }}
|
{{ related_status.published_date|timesince }}
|
||||||
|
|
|
@ -119,7 +119,7 @@
|
||||||
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-muted{% endif %}">
|
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-muted{% endif %}">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-clipped">
|
<div class="column is-clipped">
|
||||||
{% include 'snippets/status_preview.html' with status=related_status %}
|
{% include 'notifications/items/status_preview.html' with status=related_status %}
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow has-text-muted">
|
<div class="column is-narrow has-text-muted">
|
||||||
{{ related_status.published_date|timesince }}
|
{{ related_status.published_date|timesince }}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% related_status notification as related_status %}
|
{% related_status notification as related_status %}
|
||||||
|
|
||||||
{% with related_users=notification.related_users.all.distinct %}
|
{% get_related_users notification as related_users %}
|
||||||
{% with related_user_count=notification.related_users.count %}
|
{% with related_user_count=notification.related_users.count %}
|
||||||
<div class="notification {% if notification.id in unread %}has-background-primary{% endif %}">
|
<div class="notification {% if notification.id in unread %}has-background-primary{% endif %}">
|
||||||
<div class="columns is-mobile {% if notification.id in unread %}has-text-white{% else %}has-text-more-muted{% endif %}">
|
<div class="columns is-mobile {% if notification.id in unread %}has-text-white{% else %}has-text-more-muted{% endif %}">
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
{% if related_user_count > 1 %}
|
{% if related_user_count > 1 %}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<ul class="is-flex">
|
<ul class="is-flex">
|
||||||
{% for user in related_users|slice:10 %}
|
{% for user in related_users %}
|
||||||
<li class="mr-2">
|
<li class="mr-2">
|
||||||
<a href="{{ user.local_path }}">
|
<a href="{{ user.local_path }}">
|
||||||
{% include 'snippets/avatar.html' with user=user %}
|
{% include 'snippets/avatar.html' with user=user %}
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="block content">
|
<div class="block content">
|
||||||
{% if related_user_count == 1 %}
|
{% if related_user_count == 1 %}
|
||||||
{% with user=related_users.first %}
|
{% with user=related_users.0 %}
|
||||||
{% spaceless %}
|
{% spaceless %}
|
||||||
<a href="{{ user.local_path }}" class="mr-2">
|
<a href="{{ user.local_path }}" class="mr-2">
|
||||||
{% include 'snippets/avatar.html' with user=user %}
|
{% include 'snippets/avatar.html' with user=user %}
|
||||||
|
@ -37,8 +37,8 @@
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% with related_user=related_users.first.display_name %}
|
{% with related_user=related_users.0.display_name %}
|
||||||
{% with related_user_link=related_users.first.local_path %}
|
{% with related_user_link=related_users.0.local_path %}
|
||||||
{% with second_user=related_users.1.display_name %}
|
{% with second_user=related_users.1.display_name %}
|
||||||
{% with second_user_link=related_users.1.local_path %}
|
{% with second_user_link=related_users.1.local_path %}
|
||||||
{% with other_user_count=related_user_count|add:"-1" %}
|
{% with other_user_count=related_user_count|add:"-1" %}
|
||||||
|
@ -61,4 +61,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endwith %}
|
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-default{% endif %}">
|
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-default{% endif %}">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-clipped">
|
<div class="column is-clipped">
|
||||||
{% include 'snippets/status_preview.html' with status=related_status %}
|
{% include 'notifications/items/status_preview.html' with status=related_status %}
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow has-text-default">
|
<div class="column is-narrow has-text-default">
|
||||||
{{ related_status.published_date|timesince }}
|
{{ related_status.published_date|timesince }}
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-default{% endif %}">
|
<div class="notification py-2 {% if notification.id in unread %}is-primary is-light{% else %}has-background-body has-text-default{% endif %}">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-clipped">
|
<div class="column is-clipped">
|
||||||
{% include 'snippets/status_preview.html' with status=related_status %}
|
{% include 'notifications/items/status_preview.html' with status=related_status %}
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow has-text-default">
|
<div class="column is-narrow has-text-default">
|
||||||
{{ related_status.published_date|timesince }}
|
{{ related_status.published_date|timesince }}
|
||||||
|
|
|
@ -1,4 +1,17 @@
|
||||||
{% if status.content %}
|
{% load i18n %}
|
||||||
|
{% if status.content_warning %}
|
||||||
|
|
||||||
|
{% trans "Content warning" as text %}
|
||||||
|
<span>
|
||||||
|
<span class="icon icon-warning is-size-5" title="{{ text }}">
|
||||||
|
<span class="is-sr-only">{{ text }}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a href="{{ status.local_path }}">
|
||||||
|
{{ status.content_warning }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% elif status.content %}
|
||||||
<a href="{{ status.local_path }}">
|
<a href="{{ status.local_path }}">
|
||||||
{{ status.content | safe | truncatewords_html:10 }}{% if status.mention_books %} <em>{{ status.mention_books.first.title }}</em>{% endif %}
|
{{ status.content | safe | truncatewords_html:10 }}{% if status.mention_books %} <em>{{ status.mention_books.first.title }}</em>{% endif %}
|
||||||
</a>
|
</a>
|
|
@ -8,15 +8,31 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block panel %}
|
{% block panel %}
|
||||||
|
{% if success %}
|
||||||
|
<div class="notification is-success is-light">
|
||||||
|
<span class="icon icon-check" aria-hidden="true"></span>
|
||||||
|
<span>
|
||||||
|
{% trans "Successfully changed password" %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<form name="edit-profile" action="{% url 'prefs-password' %}" method="post" enctype="multipart/form-data">
|
<form name="edit-profile" action="{% url 'prefs-password' %}" method="post" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_password">{% trans "Current password:" %}</label>
|
||||||
|
{{ form.current_password }}
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.current_password.errors id="desc_current_password" %}
|
||||||
|
</div>
|
||||||
|
<hr aria-hidden="true" />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="id_password">{% trans "New password:" %}</label>
|
<label class="label" for="id_password">{% trans "New password:" %}</label>
|
||||||
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password">
|
{{ form.password }}
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.password.errors id="desc_current_password" %}
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="id_confirm_password">{% trans "Confirm password:" %}</label>
|
<label class="label" for="id_confirm_password">{% trans "Confirm password:" %}</label>
|
||||||
<input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password">
|
{{ form.confirm_password }}
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=form.confirm_password.errors id="desc_confirm_password" %}
|
||||||
</div>
|
</div>
|
||||||
<button class="button is-primary" type="submit">{% trans "Change Password" %}</button>
|
<button class="button is-primary" type="submit">{% trans "Change Password" %}</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -13,10 +13,13 @@
|
||||||
{% trans "Your export will include all the books on your shelves, books you have reviewed, and books with reading activity." %}
|
{% trans "Your export will include all the books on your shelves, books you have reviewed, and books with reading activity." %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'prefs-export-file' %}" class="button">
|
<form name="export" method="POST" href="{% url 'prefs-export' %}">
|
||||||
<span class="icon icon-download" aria-hidden="true"></span>
|
{% csrf_token %}
|
||||||
<span>Download file</span>
|
<button type="submit" class="button">
|
||||||
</a>
|
<span class="icon icon-download" aria-hidden="true"></span>
|
||||||
|
<span>{% trans "Download file" %}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
{% with results|first as local_results %}
|
{% with results|first as local_results %}
|
||||||
<ul class="block">
|
<ul class="block">
|
||||||
{% for result in local_results.results %}
|
{% for result in local_results.results %}
|
||||||
<li class="pd-4 mb-5">
|
<li class="pd-4 mb-5 local-book-search-result" id="tour-local-book-search-result">
|
||||||
<div class="columns is-mobile is-gapless mb-0">
|
<div class="columns is-mobile is-gapless mb-0">
|
||||||
<div class="column is-cover">
|
<div class="column is-cover">
|
||||||
{% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' %}
|
{% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' %}
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<details class="details-panel box" open>
|
<details class="details-panel box" open>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not result_set.connector.local %}
|
{% if not result_set.connector.local %}
|
||||||
<summary class="is-flex is-align-items-center is-flex-wrap-wrap is-gap-2">
|
<summary class="is-flex is-align-items-center is-flex-wrap-wrap is-gap-2 remote-book-search-result" id="tour-remote-search-result">
|
||||||
<span class="mb-0 title is-5">
|
<span class="mb-0 title is-5">
|
||||||
{% trans 'Results from' %}
|
{% trans 'Results from' %}
|
||||||
<a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a>
|
<a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a>
|
||||||
|
@ -102,11 +102,11 @@
|
||||||
<p class="block">
|
<p class="block">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
{% if not remote %}
|
{% if not remote %}
|
||||||
<a href="{{ request.path }}?q={{ query }}&type=book&remote=true">
|
<a href="{{ request.path }}?q={{ query }}&type=book&remote=true" id="tour-load-from-other-catalogues">
|
||||||
{% trans "Load results from other catalogues" %}
|
{% trans "Load results from other catalogues" %}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'create-book' %}">
|
<a href="{% url 'create-book' %}" id="tour-manually-add-book">
|
||||||
{% trans "Manually add book" %}
|
{% trans "Manually add book" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<form class="block" action="{% url 'search' %}" method="GET">
|
<form class="block" action="{% url 'search' %}" method="GET">
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input type="text" class="input" name="q" value="{{ query }}" aria-label="{% trans 'Search query' %}">
|
<input type="text" class="input" name="q" value="{{ query }}" aria-label="{% trans 'Search query' %}" id="tour-search-page-input">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select" aria-label="{% trans 'Search type' %}">
|
<div class="select" aria-label="{% trans 'Search type' %}">
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<section class="block">
|
<section class="block" id="search-results-block">
|
||||||
{% if not results %}
|
{% if not results %}
|
||||||
<p>
|
<p>
|
||||||
<em>{% blocktrans %}No results found for "{{ query }}"{% endblocktrans %}</em>
|
<em>{% blocktrans %}No results found for "{{ query }}"{% endblocktrans %}</em>
|
||||||
|
@ -68,3 +68,9 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/search.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -38,60 +38,39 @@
|
||||||
|
|
||||||
<div class="columns block is-multiline">
|
<div class="columns block is-multiline">
|
||||||
{% if email_config_error %}
|
{% if email_config_error %}
|
||||||
<div class="column is-flex">
|
{% include 'settings/dashboard/warnings/email_config.html' with warning_level="danger" fullwidth=True %}
|
||||||
<span class="notification is-warning is-block is-flex-grow-1">
|
|
||||||
{% blocktrans trimmed %}
|
|
||||||
Your outgoing email address, <code>{{ email_sender }}</code>, may be misconfigured.
|
|
||||||
{% endblocktrans %}
|
|
||||||
{% trans "Check the <code>EMAIL_SENDER_NAME</code> and <code>EMAIL_SENDER_DOMAIN</code> in your <code>.env</code>." %}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if reports %}
|
{% if current_version %}
|
||||||
<div class="column is-flex">
|
{% include 'settings/dashboard/warnings/update_version.html' with warning_level="warning" fullwidth=True %}
|
||||||
<a href="{% url 'settings-reports' %}" class="notification is-warning is-block is-flex-grow-1">
|
|
||||||
{% blocktrans trimmed count counter=reports with display_count=reports|intcomma %}
|
|
||||||
{{ display_count }} open report
|
|
||||||
{% plural %}
|
|
||||||
{{ display_count }} open reports
|
|
||||||
{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if pending_domains %}
|
{% if missing_privacy or missing_conduct %}
|
||||||
<div class="column is-flex">
|
<div class="column is-12 columns m-0 p-0">
|
||||||
<a href="{% url 'settings-link-domain' %}" class="notification is-primary is-block is-flex-grow-1">
|
{% if missing_privacy %}
|
||||||
{% blocktrans trimmed count counter=pending_domains with display_count=pending_domains|intcomma %}
|
{% include 'settings/dashboard/warnings/missing_privacy.html' with warning_level="danger" %}
|
||||||
{{ display_count }} domain needs review
|
{% endif %}
|
||||||
{% plural %}
|
|
||||||
{{ display_count }} domains need review
|
|
||||||
{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if not site.allow_registration and site.allow_invite_requests and invite_requests %}
|
{% if missing_conduct %}
|
||||||
<div class="column is-flex">
|
{% include 'settings/dashboard/warnings/missing_conduct.html' with warning_level="warning" %}
|
||||||
<a href="{% url 'settings-invite-requests' %}" class="notification is-block is-success is-flex-grow-1">
|
{% endif %}
|
||||||
{% blocktrans trimmed count counter=invite_requests with display_count=invite_requests|intcomma %}
|
|
||||||
{{ display_count }} invite request
|
|
||||||
{% plural %}
|
|
||||||
{{ display_count }} invite requests
|
|
||||||
{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if current_version %}
|
{% if current_version %}
|
||||||
<div class="column is-flex">
|
{% include 'settings/dashboard/warnings/update_version.html' with warning_level="warning" fullwidth=True %}
|
||||||
<a href="https://docs.joinbookwyrm.com/updating.html" class="notification is-block is-warning is-flex-grow-1" target="_blank">
|
{% endif %}
|
||||||
{% blocktrans trimmed with current=current_version available=available_version %}
|
|
||||||
An update is available! You're running v{{ current }} and the latest release is {{ available }}.
|
{% if reports %}
|
||||||
{% endblocktrans %}
|
{% include 'settings/dashboard/warnings/reports.html' with warning_level="warning" %}
|
||||||
</a>
|
{% endif %}
|
||||||
</div>
|
|
||||||
|
{% if pending_domains %}
|
||||||
|
{% include 'settings/dashboard/warnings/domain_review.html' with warning_level="primary" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not site.allow_registration and site.allow_invite_requests and invite_requests %}
|
||||||
|
{% include 'settings/dashboard/warnings/invites.html' with warning_level="success" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends 'settings/dashboard/warnings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% block warning_link %}{% url 'settings-link-domain' %}{% endblock %}
|
||||||
|
|
||||||
|
{% block warning_text %}
|
||||||
|
|
||||||
|
{% blocktrans trimmed count counter=pending_domains with display_count=pending_domains|intcomma %}
|
||||||
|
{{ display_count }} domain needs review
|
||||||
|
{% plural %}
|
||||||
|
{{ display_count }} domains need review
|
||||||
|
{% endblocktrans %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{% extends 'settings/dashboard/warnings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block warning_link %}https://docs.joinbookwyrm.com/install-prod.html{% endblock %}
|
||||||
|
|
||||||
|
{% block warning_text %}
|
||||||
|
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
Your outgoing email address, <code>{{ email_sender }}</code>, may be misconfigured.
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% trans "Check the <code>EMAIL_SENDER_NAME</code> and <code>EMAIL_SENDER_DOMAIN</code> in your <code>.env</code> file." %}
|
||||||
|
|
||||||
|
{% endblock %}
|
15
bookwyrm/templates/settings/dashboard/warnings/invites.html
Normal file
15
bookwyrm/templates/settings/dashboard/warnings/invites.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends 'settings/dashboard/warnings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% block warning_link %}{% url 'settings-invite-requests' %}{% endblock %}
|
||||||
|
|
||||||
|
{% block warning_text %}
|
||||||
|
|
||||||
|
{% blocktrans trimmed count counter=invite_requests with display_count=invite_requests|intcomma %}
|
||||||
|
{{ display_count }} invite request
|
||||||
|
{% plural %}
|
||||||
|
{{ display_count }} invite requests
|
||||||
|
{% endblocktrans %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="column is-flex{% if fullwidth %} is-12{% endif %}">
|
||||||
|
<a href="{% block warning_link %}{% endblock %}" class="notification is-{{ warning_level }} is-block is-flex-grow-1">
|
||||||
|
{% block warning_text %}{% endblock %}
|
||||||
|
</a>
|
||||||
|
</div>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends 'settings/dashboard/warnings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block warning_link %}{% url 'settings-site' %}#instance-info{% endblock %}
|
||||||
|
|
||||||
|
{% block warning_text %}
|
||||||
|
|
||||||
|
{% trans "Your instance is missing a code of conduct." %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends 'settings/dashboard/warnings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block warning_link %}{% url 'settings-site' %}#instance-info{% endblock %}
|
||||||
|
|
||||||
|
{% block warning_text %}
|
||||||
|
|
||||||
|
{% trans "Your instance is missing a privacy policy." %}
|
||||||
|
|
||||||
|
{% endblock %}
|
15
bookwyrm/templates/settings/dashboard/warnings/reports.html
Normal file
15
bookwyrm/templates/settings/dashboard/warnings/reports.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends 'settings/dashboard/warnings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% block warning_link %}{% url 'settings-reports' %}{% endblock %}
|
||||||
|
|
||||||
|
{% block warning_text %}
|
||||||
|
|
||||||
|
{% blocktrans trimmed count counter=reports with display_count=reports|intcomma %}
|
||||||
|
{{ display_count }} open report
|
||||||
|
{% plural %}
|
||||||
|
{{ display_count }} open reports
|
||||||
|
{% endblocktrans %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% extends 'settings/dashboard/warnings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block warning_link %}https://docs.joinbookwyrm.com/updating.html{% endblock %}
|
||||||
|
|
||||||
|
{% block warning_text %}
|
||||||
|
|
||||||
|
{% blocktrans trimmed with current=current_version available=available_version %}
|
||||||
|
An update is available! You're running v{{ current }} and the latest release is {{ available }}.
|
||||||
|
{% endblocktrans %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
<nav class="block columns is-mobile scroll-x">
|
<nav class="block columns is-mobile scroll-x">
|
||||||
<div class="column pr-0">
|
<div class="column pr-0">
|
||||||
<div class="tabs">
|
<div class="tabs" id="tour-user-shelves">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="{% if shelf.identifier == 'all' %}is-active{% endif %}">
|
<li class="{% if shelf.identifier == 'all' %}is-active{% endif %}">
|
||||||
<a href="{% url 'user-shelves' user|username %}"{% if shelf.identifier == 'all' %} aria-current="page"{% endif %}>
|
<a href="{% url 'user-shelves' user|username %}"{% if shelf.identifier == 'all' %} aria-current="page"{% endif %}>
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'import' %}">
|
<a href="{% url 'import' %}" id="tour-import-books">
|
||||||
<span class="icon icon-list" aria-hidden="true"></span>
|
<span class="icon icon-list" aria-hidden="true"></span>
|
||||||
<span>{% trans "Import Books" %}</span>
|
<span>{% trans "Import Books" %}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow" id="tour-create-shelf">
|
||||||
{% trans "Create shelf" as button_text %}
|
{% trans "Create shelf" as button_text %}
|
||||||
{% include 'snippets/toggle/open_button.html' with text=button_text icon_with_text="plus" controls_text="create_shelf_form" focus="create_shelf_form_header" %}
|
{% include 'snippets/toggle/open_button.html' with text=button_text icon_with_text="plus" controls_text="create_shelf_form" focus="create_shelf_form_header" %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -216,3 +216,9 @@
|
||||||
{% include 'snippets/pagination.html' with page=books path=request.path %}
|
{% include 'snippets/pagination.html' with page=books path=request.path %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/user_books.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% load utilities %}
|
{% load utilities %}
|
||||||
|
|
||||||
{% with status_type=request.GET.status_type %}
|
{% with status_type=request.GET.status_type %}
|
||||||
<div class="tab-group">
|
<div class="tab-group tour-review-comment-quote">
|
||||||
<div class="bw-tabs is-boxed" role="tablist">
|
<div class="bw-tabs is-boxed" role="tablist">
|
||||||
<a
|
<a
|
||||||
class="{% if status_type == 'review' or not status_type %}is-active{% endif %}"
|
class="{% if status_type == 'review' or not status_type %}is-active{% endif %}"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<div class="field is-relative">
|
<div class="field is-relative tour-spoiler-alert">
|
||||||
<details
|
<details
|
||||||
{% if reply_parent.content_warning or draft.content_warning %}open{% endif %}
|
{% if reply_parent.content_warning or draft.content_warning %}open{% endif %}
|
||||||
>
|
>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<form action="{% url 'unfollow' %}" method="POST" class="interaction follow_{{ user.id }} {% if not relationship.is_following and not relationship.is_follow_pending %}is-hidden{%endif %}" data-id="follow_{{ user.id }}">
|
<form action="{% url 'unfollow' %}" method="POST" class="interaction follow_{{ user.id }} {% if not relationship.is_following and not relationship.is_follow_pending %}is-hidden{%endif %}" data-id="follow_{{ user.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="user" value="{{ user.username }}">
|
<input type="hidden" name="user" value="{{ user.username }}">
|
||||||
{% if user.manually_approves_followers and not relationship.is_following %}
|
{% if relationship.is_follow_pending %}
|
||||||
<button class="button is-small is-danger is-light" type="submit">
|
<button class="button is-small is-danger is-light" type="submit">
|
||||||
{% trans "Undo follow request" %}
|
{% trans "Undo follow request" %}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load utilities %}
|
{% load utilities %}
|
||||||
<div class="select {{ class }}">
|
<div class="select {{ class }}" id="tour-privacy">
|
||||||
{% firstof privacy_uuid 0|uuid as uuid %}
|
{% firstof privacy_uuid 0|uuid as uuid %}
|
||||||
{% if not no_label %}
|
{% if not no_label %}
|
||||||
<label class="is-sr-only" for="privacy_{{ uuid }}">{% trans "Post privacy" %}</label>
|
<label class="is-sr-only" for="privacy_{{ uuid }}">{% trans "Post privacy" %}</label>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
{% if is_self %}
|
{% if is_self %}
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow" id="tour-create-group">
|
||||||
{% trans "Create group" as button_text %}
|
{% trans "Create group" as button_text %}
|
||||||
{% include 'snippets/toggle/open_button.html' with controls_text="create_group" icon_with_text="plus" text=button_text %}
|
{% include 'snippets/toggle/open_button.html' with controls_text="create_group" icon_with_text="plus" text=button_text %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,3 +35,9 @@
|
||||||
{% include 'snippets/pagination.html' with page=user.memberships path=path %}
|
{% include 'snippets/pagination.html' with page=user.memberships path=path %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/user_groups.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
{% include 'ostatus/remote_follow_button.html' with user=user %}
|
{% include 'ostatus/remote_follow_button.html' with user=user %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if is_self and user.follower_requests.all %}
|
{% if is_self and user.active_follower_requests.all %}
|
||||||
<div class="follow-requests">
|
<div class="follow-requests">
|
||||||
<h2>{% trans "Follow Requests" %}</h2>
|
<h2>{% trans "Follow Requests" %}</h2>
|
||||||
{% for requester in user.follower_requests.all %}
|
{% for requester in user.follower_requests.all %}
|
||||||
|
@ -69,25 +69,25 @@
|
||||||
{% if is_self or user.goal.exists %}
|
{% if is_self or user.goal.exists %}
|
||||||
{% now 'Y' as year %}
|
{% now 'Y' as year %}
|
||||||
{% url 'user-goal' user|username year as url %}
|
{% url 'user-goal' user|username year as url %}
|
||||||
<li{% if url in request.path %} class="is-active"{% endif %}>
|
<li{% if url in request.path %} class="is-active"{% endif %} id="tour-reading-goal">
|
||||||
<a href="{{ url }}">{% trans "Reading Goal" %}</a>
|
<a href="{{ url }}">{% trans "Reading Goal" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_self or user|has_groups %}
|
{% if is_self or user|has_groups %}
|
||||||
{% url 'user-groups' user|username as url %}
|
{% url 'user-groups' user|username as url %}
|
||||||
<li{% if url in request.path %} class="is-active"{% endif %}>
|
<li{% if url in request.path %} class="is-active"{% endif %} id="tour-groups-tab">
|
||||||
<a href="{{ url }}">{% trans "Groups" %}</a>
|
<a href="{{ url }}">{% trans "Groups" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_self or user.list_set.exists %}
|
{% if is_self or user.list_set.exists %}
|
||||||
{% url 'user-lists' user|username as url %}
|
{% url 'user-lists' user|username as url %}
|
||||||
<li{% if url in request.path %} class="is-active"{% endif %}>
|
<li{% if url in request.path %} class="is-active"{% endif %} id="tour-lists-tab">
|
||||||
<a href="{{ url }}">{% trans "Lists" %}</a>
|
<a href="{{ url }}">{% trans "Lists" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.shelf_set.exists %}
|
{% if user.shelf_set.exists %}
|
||||||
{% url 'user-shelves' user|username as url %}
|
{% url 'user-shelves' user|username as url %}
|
||||||
<li{% if url in request.path %} class="is-active"{% endif %}>
|
<li{% if url in request.path %} class="is-active"{% endif %} id="tour-shelves-tab">
|
||||||
<a href="{{ url }}">{% trans "Books" %}</a>
|
<a href="{{ url }}">{% trans "Books" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -86,3 +86,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% if request.user.show_guided_tour %}
|
||||||
|
{% include 'guided_tour/user_profile.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -63,9 +63,15 @@
|
||||||
<li class="navbar-divider" role="presentation" aria-hidden="true"> </li>
|
<li class="navbar-divider" role="presentation" aria-hidden="true"> </li>
|
||||||
|
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
<a href="{% url 'logout' %}" class="navbar-item">
|
<form
|
||||||
{% trans 'Log out' %}
|
name="logout"
|
||||||
</a>
|
method="POST"
|
||||||
|
action="{% url 'logout' %}"
|
||||||
|
class="navbar-item"
|
||||||
|
>
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit">{% trans 'Log out' %}</button>
|
||||||
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -42,11 +42,11 @@ def get_relationship(context, user_object):
|
||||||
"""caches the relationship between the logged in user and another user"""
|
"""caches the relationship between the logged in user and another user"""
|
||||||
user = context["request"].user
|
user = context["request"].user
|
||||||
return get_or_set(
|
return get_or_set(
|
||||||
f"cached-relationship-{user.id}-{user_object.id}",
|
f"relationship-{user.id}-{user_object.id}",
|
||||||
get_relationship_name,
|
get_relationship_name,
|
||||||
user,
|
user,
|
||||||
user_object,
|
user_object,
|
||||||
timeout=259200,
|
timeout=60 * 60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,3 +12,9 @@ def related_status(notification):
|
||||||
if not notification.related_status:
|
if not notification.related_status:
|
||||||
return None
|
return None
|
||||||
return load_subclass(notification.related_status)
|
return load_subclass(notification.related_status)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag(takes_context=False)
|
||||||
|
def get_related_users(notification):
|
||||||
|
"""Who actually was it who liked your post"""
|
||||||
|
return list(reversed(list(notification.related_users.distinct())))[:10]
|
||||||
|
|
|
@ -76,6 +76,17 @@ class Notification(TestCase):
|
||||||
notification.refresh_from_db()
|
notification.refresh_from_db()
|
||||||
self.assertEqual(notification.related_users.count(), 2)
|
self.assertEqual(notification.related_users.count(), 2)
|
||||||
|
|
||||||
|
def test_notify_grouping_with_dupes(self):
|
||||||
|
"""If there are multiple options to group with, don't cause an error"""
|
||||||
|
models.Notification.objects.create(
|
||||||
|
user=self.local_user, notification_type="FAVORITE"
|
||||||
|
)
|
||||||
|
models.Notification.objects.create(
|
||||||
|
user=self.local_user, notification_type="FAVORITE"
|
||||||
|
)
|
||||||
|
models.Notification.notify(self.local_user, None, notification_type="FAVORITE")
|
||||||
|
self.assertEqual(models.Notification.objects.count(), 2)
|
||||||
|
|
||||||
def test_notify_remote(self):
|
def test_notify_remote(self):
|
||||||
"""Don't create notifications for remote users"""
|
"""Don't create notifications for remote users"""
|
||||||
models.Notification.notify(
|
models.Notification.notify(
|
||||||
|
|
|
@ -104,7 +104,9 @@ class PasswordViews(TestCase):
|
||||||
"""reset from code"""
|
"""reset from code"""
|
||||||
view = views.PasswordReset.as_view()
|
view = views.PasswordReset.as_view()
|
||||||
code = models.PasswordReset.objects.create(user=self.local_user)
|
code = models.PasswordReset.objects.create(user=self.local_user)
|
||||||
request = self.factory.post("", {"password": "hi", "confirm-password": "hi"})
|
request = self.factory.post(
|
||||||
|
"", {"password": "longwordsecure", "confirm_password": "longwordsecure"}
|
||||||
|
)
|
||||||
with patch("bookwyrm.views.landing.password.login"):
|
with patch("bookwyrm.views.landing.password.login"):
|
||||||
resp = view(request, code.code)
|
resp = view(request, code.code)
|
||||||
self.assertEqual(resp.status_code, 302)
|
self.assertEqual(resp.status_code, 302)
|
||||||
|
@ -114,7 +116,9 @@ class PasswordViews(TestCase):
|
||||||
"""reset from code"""
|
"""reset from code"""
|
||||||
view = views.PasswordReset.as_view()
|
view = views.PasswordReset.as_view()
|
||||||
models.PasswordReset.objects.create(user=self.local_user)
|
models.PasswordReset.objects.create(user=self.local_user)
|
||||||
request = self.factory.post("", {"password": "hi", "confirm-password": "hi"})
|
request = self.factory.post(
|
||||||
|
"", {"password": "longwordsecure", "confirm_password": "longwordsecure"}
|
||||||
|
)
|
||||||
resp = view(request, "jhgdkfjgdf")
|
resp = view(request, "jhgdkfjgdf")
|
||||||
validate_html(resp.render())
|
validate_html(resp.render())
|
||||||
self.assertTrue(models.PasswordReset.objects.exists())
|
self.assertTrue(models.PasswordReset.objects.exists())
|
||||||
|
@ -123,7 +127,18 @@ class PasswordViews(TestCase):
|
||||||
"""reset from code"""
|
"""reset from code"""
|
||||||
view = views.PasswordReset.as_view()
|
view = views.PasswordReset.as_view()
|
||||||
code = models.PasswordReset.objects.create(user=self.local_user)
|
code = models.PasswordReset.objects.create(user=self.local_user)
|
||||||
request = self.factory.post("", {"password": "hi", "confirm-password": "hihi"})
|
request = self.factory.post(
|
||||||
|
"", {"password": "longwordsecure", "confirm_password": "hihi"}
|
||||||
|
)
|
||||||
|
resp = view(request, code.code)
|
||||||
|
validate_html(resp.render())
|
||||||
|
self.assertTrue(models.PasswordReset.objects.exists())
|
||||||
|
|
||||||
|
def test_password_reset_invalid(self):
|
||||||
|
"""reset from code"""
|
||||||
|
view = views.PasswordReset.as_view()
|
||||||
|
code = models.PasswordReset.objects.create(user=self.local_user)
|
||||||
|
request = self.factory.post("", {"password": "a", "confirm_password": "a"})
|
||||||
resp = view(request, code.code)
|
resp = view(request, code.code)
|
||||||
validate_html(resp.render())
|
validate_html(resp.render())
|
||||||
self.assertTrue(models.PasswordReset.objects.exists())
|
self.assertTrue(models.PasswordReset.objects.exists())
|
||||||
|
|
|
@ -122,6 +122,17 @@ class RegisterViews(TestCase):
|
||||||
self.assertEqual(models.User.objects.count(), 1)
|
self.assertEqual(models.User.objects.count(), 1)
|
||||||
validate_html(response.render())
|
validate_html(response.render())
|
||||||
|
|
||||||
|
def test_register_invalid_password(self, *_):
|
||||||
|
"""gotta have an email"""
|
||||||
|
view = views.Register.as_view()
|
||||||
|
self.assertEqual(models.User.objects.count(), 1)
|
||||||
|
request = self.factory.post(
|
||||||
|
"register/", {"localname": "nutria", "password": "password", "email": "aa"}
|
||||||
|
)
|
||||||
|
response = view(request)
|
||||||
|
self.assertEqual(models.User.objects.count(), 1)
|
||||||
|
validate_html(response.render())
|
||||||
|
|
||||||
def test_register_error_and_invite(self, *_):
|
def test_register_error_and_invite(self, *_):
|
||||||
"""redirect to the invite page"""
|
"""redirect to the invite page"""
|
||||||
view = views.Register.as_view()
|
view = views.Register.as_view()
|
||||||
|
|
|
@ -42,17 +42,71 @@ class ChangePasswordViews(TestCase):
|
||||||
"""change password"""
|
"""change password"""
|
||||||
view = views.ChangePassword.as_view()
|
view = views.ChangePassword.as_view()
|
||||||
password_hash = self.local_user.password
|
password_hash = self.local_user.password
|
||||||
request = self.factory.post("", {"password": "hi", "confirm-password": "hi"})
|
request = self.factory.post(
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
"current_password": "password",
|
||||||
|
"password": "longwordsecure",
|
||||||
|
"confirm_password": "longwordsecure",
|
||||||
|
},
|
||||||
|
)
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
with patch("bookwyrm.views.preferences.change_password.login"):
|
with patch("bookwyrm.views.preferences.change_password.login"):
|
||||||
view(request)
|
result = view(request)
|
||||||
|
validate_html(result.render())
|
||||||
|
self.local_user.refresh_from_db()
|
||||||
self.assertNotEqual(self.local_user.password, password_hash)
|
self.assertNotEqual(self.local_user.password, password_hash)
|
||||||
|
|
||||||
|
def test_password_change_wrong_current(self):
|
||||||
|
"""change password"""
|
||||||
|
view = views.ChangePassword.as_view()
|
||||||
|
password_hash = self.local_user.password
|
||||||
|
request = self.factory.post(
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
"current_password": "not my password",
|
||||||
|
"password": "longwordsecure",
|
||||||
|
"confirm_password": "hihi",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
request.user = self.local_user
|
||||||
|
result = view(request)
|
||||||
|
validate_html(result.render())
|
||||||
|
self.local_user.refresh_from_db()
|
||||||
|
self.assertEqual(self.local_user.password, password_hash)
|
||||||
|
|
||||||
def test_password_change_mismatch(self):
|
def test_password_change_mismatch(self):
|
||||||
"""change password"""
|
"""change password"""
|
||||||
view = views.ChangePassword.as_view()
|
view = views.ChangePassword.as_view()
|
||||||
password_hash = self.local_user.password
|
password_hash = self.local_user.password
|
||||||
request = self.factory.post("", {"password": "hi", "confirm-password": "hihi"})
|
request = self.factory.post(
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
"current_password": "password",
|
||||||
|
"password": "longwordsecure",
|
||||||
|
"confirm_password": "hihi",
|
||||||
|
},
|
||||||
|
)
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
view(request)
|
result = view(request)
|
||||||
|
validate_html(result.render())
|
||||||
|
self.local_user.refresh_from_db()
|
||||||
|
self.assertEqual(self.local_user.password, password_hash)
|
||||||
|
|
||||||
|
def test_password_change_invalid(self):
|
||||||
|
"""change password"""
|
||||||
|
view = views.ChangePassword.as_view()
|
||||||
|
password_hash = self.local_user.password
|
||||||
|
request = self.factory.post(
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
"current_password": "password",
|
||||||
|
"password": "hi",
|
||||||
|
"confirm_password": "hi",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
request.user = self.local_user
|
||||||
|
result = view(request)
|
||||||
|
validate_html(result.render())
|
||||||
|
self.local_user.refresh_from_db()
|
||||||
self.assertEqual(self.local_user.password, password_hash)
|
self.assertEqual(self.local_user.password, password_hash)
|
||||||
|
|
|
@ -54,9 +54,9 @@ class ExportViews(TestCase):
|
||||||
user=self.local_user,
|
user=self.local_user,
|
||||||
book=self.book,
|
book=self.book,
|
||||||
)
|
)
|
||||||
request = self.factory.get("")
|
request = self.factory.post("")
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
export = views.export_user_book_data(request)
|
export = views.Export.as_view()(request)
|
||||||
self.assertIsInstance(export, StreamingHttpResponse)
|
self.assertIsInstance(export, StreamingHttpResponse)
|
||||||
self.assertEqual(export.status_code, 200)
|
self.assertEqual(export.status_code, 200)
|
||||||
result = list(export.streaming_content)
|
result = list(export.streaming_content)
|
|
@ -32,6 +32,14 @@ class ShelfActionViews(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
remote_id="https://example.com/users/mouse",
|
remote_id="https://example.com/users/mouse",
|
||||||
)
|
)
|
||||||
|
self.another_user = models.User.objects.create_user(
|
||||||
|
"rat@local.com",
|
||||||
|
"rat@rat.com",
|
||||||
|
"ratword",
|
||||||
|
local=True,
|
||||||
|
localname="rat",
|
||||||
|
remote_id="https://example.com/users/rat",
|
||||||
|
)
|
||||||
self.work = models.Work.objects.create(title="Test Work")
|
self.work = models.Work.objects.create(title="Test Work")
|
||||||
self.book = models.Edition.objects.create(
|
self.book = models.Edition.objects.create(
|
||||||
title="Example Edition",
|
title="Example Edition",
|
||||||
|
@ -66,7 +74,7 @@ class ShelfActionViews(TestCase):
|
||||||
|
|
||||||
def test_shelve_to_read(self, *_):
|
def test_shelve_to_read(self, *_):
|
||||||
"""special behavior for the to-read shelf"""
|
"""special behavior for the to-read shelf"""
|
||||||
shelf = models.Shelf.objects.get(identifier="to-read")
|
shelf = models.Shelf.objects.get(user=self.local_user, identifier="to-read")
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
"", {"book": self.book.id, "shelf": shelf.identifier}
|
"", {"book": self.book.id, "shelf": shelf.identifier}
|
||||||
)
|
)
|
||||||
|
@ -79,7 +87,7 @@ class ShelfActionViews(TestCase):
|
||||||
|
|
||||||
def test_shelve_reading(self, *_):
|
def test_shelve_reading(self, *_):
|
||||||
"""special behavior for the reading shelf"""
|
"""special behavior for the reading shelf"""
|
||||||
shelf = models.Shelf.objects.get(identifier="reading")
|
shelf = models.Shelf.objects.get(user=self.local_user, identifier="reading")
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
"", {"book": self.book.id, "shelf": shelf.identifier}
|
"", {"book": self.book.id, "shelf": shelf.identifier}
|
||||||
)
|
)
|
||||||
|
@ -92,7 +100,7 @@ class ShelfActionViews(TestCase):
|
||||||
|
|
||||||
def test_shelve_read(self, *_):
|
def test_shelve_read(self, *_):
|
||||||
"""special behavior for the read shelf"""
|
"""special behavior for the read shelf"""
|
||||||
shelf = models.Shelf.objects.get(identifier="read")
|
shelf = models.Shelf.objects.get(user=self.local_user, identifier="read")
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
"", {"book": self.book.id, "shelf": shelf.identifier}
|
"", {"book": self.book.id, "shelf": shelf.identifier}
|
||||||
)
|
)
|
||||||
|
@ -105,11 +113,13 @@ class ShelfActionViews(TestCase):
|
||||||
|
|
||||||
def test_shelve_read_with_change_shelf(self, *_):
|
def test_shelve_read_with_change_shelf(self, *_):
|
||||||
"""special behavior for the read shelf"""
|
"""special behavior for the read shelf"""
|
||||||
previous_shelf = models.Shelf.objects.get(identifier="reading")
|
previous_shelf = models.Shelf.objects.get(
|
||||||
|
user=self.local_user, identifier="reading"
|
||||||
|
)
|
||||||
models.ShelfBook.objects.create(
|
models.ShelfBook.objects.create(
|
||||||
shelf=previous_shelf, user=self.local_user, book=self.book
|
shelf=previous_shelf, user=self.local_user, book=self.book
|
||||||
)
|
)
|
||||||
shelf = models.Shelf.objects.get(identifier="read")
|
shelf = models.Shelf.objects.get(user=self.local_user, identifier="read")
|
||||||
|
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
"",
|
"",
|
||||||
|
@ -160,11 +170,24 @@ class ShelfActionViews(TestCase):
|
||||||
|
|
||||||
views.create_shelf(request)
|
views.create_shelf(request)
|
||||||
|
|
||||||
shelf = models.Shelf.objects.get(name="new shelf name")
|
shelf = models.Shelf.objects.get(user=self.local_user, name="new shelf name")
|
||||||
self.assertEqual(shelf.privacy, "unlisted")
|
self.assertEqual(shelf.privacy, "unlisted")
|
||||||
self.assertEqual(shelf.description, "desc")
|
self.assertEqual(shelf.description, "desc")
|
||||||
self.assertEqual(shelf.user, self.local_user)
|
self.assertEqual(shelf.user, self.local_user)
|
||||||
|
|
||||||
|
def test_create_shelf_wrong_user(self, *_):
|
||||||
|
"""a brand new custom shelf"""
|
||||||
|
form = forms.ShelfForm()
|
||||||
|
form.data["user"] = self.another_user.id
|
||||||
|
form.data["name"] = "new shelf name"
|
||||||
|
form.data["description"] = "desc"
|
||||||
|
form.data["privacy"] = "unlisted"
|
||||||
|
request = self.factory.post("", form.data)
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
with self.assertRaises(PermissionDenied):
|
||||||
|
views.create_shelf(request)
|
||||||
|
|
||||||
def test_delete_shelf(self, *_):
|
def test_delete_shelf(self, *_):
|
||||||
"""delete a brand new custom shelf"""
|
"""delete a brand new custom shelf"""
|
||||||
request = self.factory.post("")
|
request = self.factory.post("")
|
||||||
|
@ -177,18 +200,8 @@ class ShelfActionViews(TestCase):
|
||||||
|
|
||||||
def test_delete_shelf_unauthorized(self, *_):
|
def test_delete_shelf_unauthorized(self, *_):
|
||||||
"""delete a brand new custom shelf"""
|
"""delete a brand new custom shelf"""
|
||||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
|
|
||||||
"bookwyrm.activitystreams.populate_stream_task.delay"
|
|
||||||
), patch("bookwyrm.lists_stream.populate_lists_task.delay"):
|
|
||||||
rat = models.User.objects.create_user(
|
|
||||||
"rat@local.com",
|
|
||||||
"rat@mouse.mouse",
|
|
||||||
"password",
|
|
||||||
local=True,
|
|
||||||
localname="rat",
|
|
||||||
)
|
|
||||||
request = self.factory.post("")
|
request = self.factory.post("")
|
||||||
request.user = rat
|
request.user = self.another_user
|
||||||
|
|
||||||
with self.assertRaises(PermissionDenied):
|
with self.assertRaises(PermissionDenied):
|
||||||
views.delete_shelf(request, self.shelf.id)
|
views.delete_shelf(request, self.shelf.id)
|
||||||
|
|
|
@ -10,12 +10,13 @@ from bookwyrm.settings import DOMAIN
|
||||||
from bookwyrm.tests.validate_html import validate_html
|
from bookwyrm.tests.validate_html import validate_html
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
|
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
|
||||||
@patch("bookwyrm.lists_stream.populate_lists_task.delay")
|
@patch("bookwyrm.lists_stream.populate_lists_task.delay")
|
||||||
@patch("bookwyrm.activitystreams.remove_status_task.delay")
|
@patch("bookwyrm.activitystreams.remove_status_task.delay")
|
||||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
|
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# pylint: disable=too-many-public-methods
|
||||||
class StatusViews(TestCase):
|
class StatusViews(TestCase):
|
||||||
"""viewing and creating statuses"""
|
"""viewing and creating statuses"""
|
||||||
|
|
||||||
|
@ -75,6 +76,44 @@ class StatusViews(TestCase):
|
||||||
self.assertEqual(status.book, self.book)
|
self.assertEqual(status.book, self.book)
|
||||||
self.assertIsNone(status.edited_date)
|
self.assertIsNone(status.edited_date)
|
||||||
|
|
||||||
|
def test_create_status_rating(self, *_):
|
||||||
|
"""create a status"""
|
||||||
|
view = views.CreateStatus.as_view()
|
||||||
|
form = forms.RatingForm(
|
||||||
|
{
|
||||||
|
"user": self.local_user.id,
|
||||||
|
"rating": 4,
|
||||||
|
"book": self.book.id,
|
||||||
|
"privacy": "public",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
request = self.factory.post("", form.data)
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
view(request, "rating")
|
||||||
|
|
||||||
|
status = models.ReviewRating.objects.get()
|
||||||
|
self.assertEqual(status.user, self.local_user)
|
||||||
|
self.assertEqual(status.book, self.book)
|
||||||
|
self.assertEqual(status.rating, 4.0)
|
||||||
|
self.assertIsNone(status.edited_date)
|
||||||
|
|
||||||
|
def test_create_status_wrong_user(self, *_):
|
||||||
|
"""You can't compose statuses for someone else"""
|
||||||
|
view = views.CreateStatus.as_view()
|
||||||
|
form = forms.CommentForm(
|
||||||
|
{
|
||||||
|
"content": "hi",
|
||||||
|
"user": self.remote_user.id,
|
||||||
|
"book": self.book.id,
|
||||||
|
"privacy": "public",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
request = self.factory.post("", form.data)
|
||||||
|
request.user = self.local_user
|
||||||
|
with self.assertRaises(PermissionDenied):
|
||||||
|
view(request, "comment")
|
||||||
|
|
||||||
def test_create_status_reply(self, *_):
|
def test_create_status_reply(self, *_):
|
||||||
"""create a status in reply to an existing status"""
|
"""create a status in reply to an existing status"""
|
||||||
view = views.CreateStatus.as_view()
|
view = views.CreateStatus.as_view()
|
||||||
|
|
|
@ -482,11 +482,6 @@ urlpatterns = [
|
||||||
name="prefs-password",
|
name="prefs-password",
|
||||||
),
|
),
|
||||||
re_path(r"^preferences/export/?$", views.Export.as_view(), name="prefs-export"),
|
re_path(r"^preferences/export/?$", views.Export.as_view(), name="prefs-export"),
|
||||||
re_path(
|
|
||||||
r"^preferences/export/file/?$",
|
|
||||||
views.export_user_book_data,
|
|
||||||
name="prefs-export-file",
|
|
||||||
),
|
|
||||||
re_path(r"^preferences/delete/?$", views.DeleteUser.as_view(), name="prefs-delete"),
|
re_path(r"^preferences/delete/?$", views.DeleteUser.as_view(), name="prefs-delete"),
|
||||||
re_path(r"^preferences/block/?$", views.Block.as_view(), name="prefs-block"),
|
re_path(r"^preferences/block/?$", views.Block.as_view(), name="prefs-block"),
|
||||||
re_path(r"^block/(?P<user_id>\d+)/?$", views.Block.as_view()),
|
re_path(r"^block/(?P<user_id>\d+)/?$", views.Block.as_view()),
|
||||||
|
@ -650,4 +645,5 @@ urlpatterns = [
|
||||||
re_path(
|
re_path(
|
||||||
r"^summary_revoke_key/?$", views.summary_revoke_key, name="summary-revoke-key"
|
r"^summary_revoke_key/?$", views.summary_revoke_key, name="summary-revoke-key"
|
||||||
),
|
),
|
||||||
|
path("guided-tour/<tour>", views.toggle_guided_tour),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
@ -28,7 +28,7 @@ from .admin.user_admin import UserAdmin, UserAdminList
|
||||||
# user preferences
|
# user preferences
|
||||||
from .preferences.change_password import ChangePassword
|
from .preferences.change_password import ChangePassword
|
||||||
from .preferences.edit_user import EditUser
|
from .preferences.edit_user import EditUser
|
||||||
from .preferences.export import Export, export_user_book_data
|
from .preferences.export import Export
|
||||||
from .preferences.delete_user import DeleteUser
|
from .preferences.delete_user import DeleteUser
|
||||||
from .preferences.block import Block, unblock
|
from .preferences.block import Block, unblock
|
||||||
|
|
||||||
|
@ -127,7 +127,14 @@ from .setup import InstanceConfig, CreateAdmin
|
||||||
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress
|
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress
|
||||||
from .status import edit_readthrough
|
from .status import edit_readthrough
|
||||||
from .updates import get_notification_count, get_unread_status_string
|
from .updates import get_notification_count, get_unread_status_string
|
||||||
from .user import User, Followers, Following, hide_suggestions, user_redirect
|
from .user import (
|
||||||
|
User,
|
||||||
|
Followers,
|
||||||
|
Following,
|
||||||
|
hide_suggestions,
|
||||||
|
user_redirect,
|
||||||
|
toggle_guided_tour,
|
||||||
|
)
|
||||||
from .wellknown import *
|
from .wellknown import *
|
||||||
from .annual_summary import (
|
from .annual_summary import (
|
||||||
AnnualSummary,
|
AnnualSummary,
|
||||||
|
|
|
@ -42,6 +42,19 @@ class Dashboard(View):
|
||||||
"email_sender"
|
"email_sender"
|
||||||
] = f"{settings.EMAIL_SENDER_NAME}@{settings.EMAIL_SENDER_DOMAIN}"
|
] = f"{settings.EMAIL_SENDER_NAME}@{settings.EMAIL_SENDER_DOMAIN}"
|
||||||
|
|
||||||
|
site = models.SiteSettings.objects.get()
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
data["missing_conduct"] = (
|
||||||
|
not site.code_of_conduct
|
||||||
|
or site.code_of_conduct
|
||||||
|
== site._meta.get_field("code_of_conduct").get_default()
|
||||||
|
)
|
||||||
|
data["missing_privacy"] = (
|
||||||
|
not site.privacy_policy
|
||||||
|
or site.privacy_policy
|
||||||
|
== site._meta.get_field("privacy_policy").get_default()
|
||||||
|
)
|
||||||
|
|
||||||
# check version
|
# check version
|
||||||
try:
|
try:
|
||||||
release = get_data(settings.RELEASE_API, timeout=3)
|
release = get_data(settings.RELEASE_API, timeout=3)
|
||||||
|
|
|
@ -65,6 +65,7 @@ class Feed(View):
|
||||||
"filters_applied": filters_applied,
|
"filters_applied": filters_applied,
|
||||||
"path": f"/{tab['key']}",
|
"path": f"/{tab['key']}",
|
||||||
"annual_summary_year": get_annual_summary_year(),
|
"annual_summary_year": get_annual_summary_year(),
|
||||||
|
"has_tour": True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, "feed/feed.html", data)
|
return TemplateResponse(request, "feed/feed.html", data)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
""" views for actions you can take in the application """
|
""" views for actions you can take in the application """
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
@ -13,6 +15,7 @@ from .helpers import (
|
||||||
handle_remote_webfinger,
|
handle_remote_webfinger,
|
||||||
subscribe_remote_webfinger,
|
subscribe_remote_webfinger,
|
||||||
WebFingerError,
|
WebFingerError,
|
||||||
|
is_api_request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,6 +37,8 @@ def follow(request):
|
||||||
# that means we should save to trigger a re-broadcast
|
# that means we should save to trigger a re-broadcast
|
||||||
follow_request.save()
|
follow_request.save()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
return HttpResponse()
|
||||||
return redirect(to_follow.local_path)
|
return redirect(to_follow.local_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,8 +63,10 @@ def unfollow(request):
|
||||||
except models.UserFollowRequest.DoesNotExist:
|
except models.UserFollowRequest.DoesNotExist:
|
||||||
clear_cache(request.user, to_unfollow)
|
clear_cache(request.user, to_unfollow)
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
return HttpResponse()
|
||||||
# this is handled with ajax so it shouldn't really matter
|
# this is handled with ajax so it shouldn't really matter
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Goal(View):
|
||||||
privacy=goal.privacy,
|
privacy=goal.privacy,
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("user-goal", request.user.localname, year)
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
|
@ -79,4 +79,4 @@ def hide_goal(request):
|
||||||
"""don't keep bugging people to set a goal"""
|
"""don't keep bugging people to set a goal"""
|
||||||
request.user.show_goal = False
|
request.user.show_goal = False
|
||||||
request.user.save(broadcast=False, update_fields=["show_goal"])
|
request.user.save(broadcast=False, update_fields=["show_goal"])
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Favorite(View):
|
||||||
|
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@ -48,7 +48,7 @@ class Unfavorite(View):
|
||||||
favorite.delete()
|
favorite.delete()
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@ -67,7 +67,7 @@ class Boost(View):
|
||||||
boosted_status=status, user=request.user
|
boosted_status=status, user=request.user
|
||||||
).exists():
|
).exists():
|
||||||
# you already boosted that.
|
# you already boosted that.
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
models.Boost.objects.create(
|
models.Boost.objects.create(
|
||||||
boosted_status=status,
|
boosted_status=status,
|
||||||
|
@ -76,7 +76,7 @@ class Boost(View):
|
||||||
)
|
)
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@ -94,4 +94,4 @@ class Unboost(View):
|
||||||
boost.delete()
|
boost.delete()
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
|
@ -58,7 +58,7 @@ class Login(View):
|
||||||
user.update_active_date()
|
user.update_active_date()
|
||||||
if request.POST.get("first_login"):
|
if request.POST.get("first_login"):
|
||||||
return set_language(user, redirect("get-started-profile"))
|
return set_language(user, redirect("get-started-profile"))
|
||||||
return set_language(user, redirect(request.GET.get("next", "/")))
|
return set_language(user, redirect("/"))
|
||||||
|
|
||||||
# maybe the user is pending email confirmation
|
# maybe the user is pending email confirmation
|
||||||
if models.User.objects.filter(
|
if models.User.objects.filter(
|
||||||
|
@ -77,7 +77,7 @@ class Login(View):
|
||||||
class Logout(View):
|
class Logout(View):
|
||||||
"""log out"""
|
"""log out"""
|
||||||
|
|
||||||
def get(self, request):
|
def post(self, request):
|
||||||
"""done with this place! outa here!"""
|
"""done with this place! outa here!"""
|
||||||
logout(request)
|
logout(request)
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.shortcuts import redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import forms, models
|
||||||
from bookwyrm.emailing import password_reset_email
|
from bookwyrm.emailing import password_reset_email
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,7 +57,8 @@ class PasswordReset(View):
|
||||||
except models.PasswordReset.DoesNotExist:
|
except models.PasswordReset.DoesNotExist:
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
return TemplateResponse(request, "landing/password_reset.html", {"code": code})
|
data = {"code": code, "form": forms.PasswordResetForm()}
|
||||||
|
return TemplateResponse(request, "landing/password_reset.html", data)
|
||||||
|
|
||||||
def post(self, request, code):
|
def post(self, request, code):
|
||||||
"""allow a user to change their password through an emailed token"""
|
"""allow a user to change their password through an emailed token"""
|
||||||
|
@ -68,14 +69,12 @@ class PasswordReset(View):
|
||||||
return TemplateResponse(request, "landing/password_reset.html", data)
|
return TemplateResponse(request, "landing/password_reset.html", data)
|
||||||
|
|
||||||
user = reset_code.user
|
user = reset_code.user
|
||||||
|
form = forms.PasswordResetForm(request.POST, instance=user)
|
||||||
new_password = request.POST.get("password")
|
if not form.is_valid():
|
||||||
confirm_password = request.POST.get("confirm-password")
|
data = {"code": code, "form": form}
|
||||||
|
|
||||||
if new_password != confirm_password:
|
|
||||||
data = {"errors": ["Passwords do not match"]}
|
|
||||||
return TemplateResponse(request, "landing/password_reset.html", data)
|
return TemplateResponse(request, "landing/password_reset.html", data)
|
||||||
|
|
||||||
|
new_password = form.cleaned_data["password"]
|
||||||
user.set_password(new_password)
|
user.set_password(new_password)
|
||||||
user.save(broadcast=False, update_fields=["password"])
|
user.save(broadcast=False, update_fields=["password"])
|
||||||
login(request, user)
|
login(request, user)
|
||||||
|
|
|
@ -134,19 +134,19 @@ class ConfirmEmail(View):
|
||||||
class ResendConfirmEmail(View):
|
class ResendConfirmEmail(View):
|
||||||
"""you probably didn't get the email because celery is slow but you can try this"""
|
"""you probably didn't get the email because celery is slow but you can try this"""
|
||||||
|
|
||||||
def get(self, request, error=False):
|
def get(self, request):
|
||||||
"""resend link landing page"""
|
"""resend link landing page"""
|
||||||
return TemplateResponse(request, "confirm_email/resend.html", {"error": error})
|
return TemplateResponse(request, "confirm_email/resend.html")
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""resend confirmation link"""
|
"""resend confirmation link"""
|
||||||
email = request.POST.get("email")
|
email = request.POST.get("email")
|
||||||
try:
|
try:
|
||||||
user = models.User.objects.get(email=email)
|
user = models.User.objects.get(email=email)
|
||||||
|
emailing.email_confirmation_email(user)
|
||||||
except models.User.DoesNotExist:
|
except models.User.DoesNotExist:
|
||||||
return self.get(request, error=True)
|
pass
|
||||||
|
|
||||||
emailing.email_confirmation_email(user)
|
|
||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request, "confirm_email/confirm_email.html", {"valid": True}
|
request, "confirm_email/confirm_email.html", {"valid": True}
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,7 +17,10 @@ class Lists(View):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""display a book list"""
|
"""display a book list"""
|
||||||
lists = ListsStream().get_list_stream(request.user)
|
if request.user.is_authenticated:
|
||||||
|
lists = ListsStream().get_list_stream(request.user)
|
||||||
|
else:
|
||||||
|
lists = models.List.objects.filter(privacy="public")
|
||||||
paginated = Paginator(lists, 12)
|
paginated = Paginator(lists, 12)
|
||||||
data = {
|
data = {
|
||||||
"lists": paginated.get_page(request.GET.get("page")),
|
"lists": paginated.get_page(request.GET.get("page")),
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
""" class views for password management """
|
""" class views for password management """
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
from django.views.decorators.debug import sensitive_variables, sensitive_post_parameters
|
||||||
|
|
||||||
|
from bookwyrm import forms
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable= no-self-use
|
# pylint: disable= no-self-use
|
||||||
|
@ -14,18 +16,24 @@ class ChangePassword(View):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""change password page"""
|
"""change password page"""
|
||||||
data = {"user": request.user}
|
data = {"form": forms.ChangePasswordForm()}
|
||||||
return TemplateResponse(request, "preferences/change_password.html", data)
|
return TemplateResponse(request, "preferences/change_password.html", data)
|
||||||
|
|
||||||
|
@method_decorator(sensitive_variables("new_password"))
|
||||||
|
@method_decorator(sensitive_post_parameters("current_password"))
|
||||||
|
@method_decorator(sensitive_post_parameters("password"))
|
||||||
|
@method_decorator(sensitive_post_parameters("confirm_password"))
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""allow a user to change their password"""
|
"""allow a user to change their password"""
|
||||||
new_password = request.POST.get("password")
|
form = forms.ChangePasswordForm(request.POST, instance=request.user)
|
||||||
confirm_password = request.POST.get("confirm-password")
|
if not form.is_valid():
|
||||||
|
data = {"form": form}
|
||||||
if new_password != confirm_password:
|
return TemplateResponse(request, "preferences/change_password.html", data)
|
||||||
return redirect("prefs-password")
|
|
||||||
|
|
||||||
|
new_password = form.cleaned_data["password"]
|
||||||
request.user.set_password(new_password)
|
request.user.set_password(new_password)
|
||||||
request.user.save(broadcast=False, update_fields=["password"])
|
request.user.save(broadcast=False, update_fields=["password"])
|
||||||
|
|
||||||
login(request, request.user)
|
login(request, request.user)
|
||||||
return redirect("user-feed", request.user.localname)
|
data = {"success": True, "form": forms.ChangePasswordForm()}
|
||||||
|
return TemplateResponse(request, "preferences/change_password.html", data)
|
||||||
|
|
|
@ -7,7 +7,6 @@ from django.http import StreamingHttpResponse
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.http import require_GET
|
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
@ -20,35 +19,34 @@ class Export(View):
|
||||||
"""Request csv file"""
|
"""Request csv file"""
|
||||||
return TemplateResponse(request, "preferences/export.html")
|
return TemplateResponse(request, "preferences/export.html")
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
@login_required
|
"""Streaming the csv file of a user's book data"""
|
||||||
@require_GET
|
data = (
|
||||||
def export_user_book_data(request):
|
models.Edition.viewer_aware_objects(request.user)
|
||||||
"""Streaming the csv file of a user's book data"""
|
.filter(
|
||||||
data = (
|
Q(shelves__user=request.user)
|
||||||
models.Edition.viewer_aware_objects(request.user)
|
| Q(readthrough__user=request.user)
|
||||||
.filter(
|
| Q(review__user=request.user)
|
||||||
Q(shelves__user=request.user)
|
| Q(comment__user=request.user)
|
||||||
| Q(readthrough__user=request.user)
|
| Q(quotation__user=request.user)
|
||||||
| Q(review__user=request.user)
|
)
|
||||||
| Q(comment__user=request.user)
|
.distinct()
|
||||||
| Q(quotation__user=request.user)
|
|
||||||
)
|
)
|
||||||
.distinct()
|
|
||||||
)
|
|
||||||
|
|
||||||
generator = csv_row_generator(data, request.user)
|
generator = csv_row_generator(data, request.user)
|
||||||
|
|
||||||
pseudo_buffer = Echo()
|
pseudo_buffer = Echo()
|
||||||
writer = csv.writer(pseudo_buffer)
|
writer = csv.writer(pseudo_buffer)
|
||||||
# for testing, if you want to see the results in the browser:
|
# for testing, if you want to see the results in the browser:
|
||||||
# from django.http import JsonResponse
|
# from django.http import JsonResponse
|
||||||
# return JsonResponse(list(generator), safe=False)
|
# return JsonResponse(list(generator), safe=False)
|
||||||
return StreamingHttpResponse(
|
return StreamingHttpResponse(
|
||||||
(writer.writerow(row) for row in generator),
|
(writer.writerow(row) for row in generator),
|
||||||
content_type="text/csv",
|
content_type="text/csv",
|
||||||
headers={"Content-Disposition": 'attachment; filename="bookwyrm-export.csv"'},
|
headers={
|
||||||
)
|
"Content-Disposition": 'attachment; filename="bookwyrm-export.csv"'
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def csv_row_generator(books, user):
|
def csv_row_generator(books, user):
|
||||||
|
|
|
@ -79,13 +79,11 @@ class ReadingStatus(View):
|
||||||
current_status_shelfbook = shelves[0] if shelves else None
|
current_status_shelfbook = shelves[0] if shelves else None
|
||||||
|
|
||||||
# checking the referer prevents redirecting back to the modal page
|
# checking the referer prevents redirecting back to the modal page
|
||||||
referer = request.headers.get("Referer", "/")
|
|
||||||
referer = "/" if "reading-status" in referer else referer
|
|
||||||
if current_status_shelfbook is not None:
|
if current_status_shelfbook is not None:
|
||||||
if current_status_shelfbook.shelf.identifier != desired_shelf.identifier:
|
if current_status_shelfbook.shelf.identifier != desired_shelf.identifier:
|
||||||
current_status_shelfbook.delete()
|
current_status_shelfbook.delete()
|
||||||
else: # It already was on the shelf
|
else: # It already was on the shelf
|
||||||
return redirect(referer)
|
return redirect("/")
|
||||||
|
|
||||||
models.ShelfBook.objects.create(
|
models.ShelfBook.objects.create(
|
||||||
book=book, shelf=desired_shelf, user=request.user
|
book=book, shelf=desired_shelf, user=request.user
|
||||||
|
@ -123,7 +121,7 @@ class ReadingStatus(View):
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
|
|
||||||
return redirect(referer)
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@ -205,7 +203,7 @@ def delete_readthrough(request):
|
||||||
readthrough.raise_not_deletable(request.user)
|
readthrough.raise_not_deletable(request.user)
|
||||||
|
|
||||||
readthrough.delete()
|
readthrough.delete()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -216,4 +214,4 @@ def delete_progressupdate(request):
|
||||||
update.raise_not_deletable(request.user)
|
update.raise_not_deletable(request.user)
|
||||||
|
|
||||||
update.delete()
|
update.delete()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
|
@ -13,9 +13,11 @@ def create_shelf(request):
|
||||||
"""user generated shelves"""
|
"""user generated shelves"""
|
||||||
form = forms.ShelfForm(request.POST)
|
form = forms.ShelfForm(request.POST)
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("user-shelves", request.user.localname)
|
||||||
|
|
||||||
shelf = form.save()
|
shelf = form.save(commit=False)
|
||||||
|
shelf.raise_not_editable(request.user)
|
||||||
|
shelf.save()
|
||||||
return redirect(shelf.local_path)
|
return redirect(shelf.local_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,7 +72,7 @@ def shelve(request):
|
||||||
):
|
):
|
||||||
current_read_status_shelfbook.delete()
|
current_read_status_shelfbook.delete()
|
||||||
else: # It is already on the shelf
|
else: # It is already on the shelf
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
# create the new shelf-book entry
|
# create the new shelf-book entry
|
||||||
models.ShelfBook.objects.create(
|
models.ShelfBook.objects.create(
|
||||||
|
@ -86,7 +88,7 @@ def shelve(request):
|
||||||
# Might be good to alert, or reject the action?
|
# Might be good to alert, or reject the action?
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -100,4 +102,4 @@ def unshelve(request, book_id=False):
|
||||||
)
|
)
|
||||||
shelf_book.raise_not_deletable(request.user)
|
shelf_book.raise_not_deletable(request.user)
|
||||||
shelf_book.delete()
|
shelf_book.delete()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
|
@ -82,9 +82,10 @@ class CreateStatus(View):
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
logger.exception(form.errors)
|
logger.exception(form.errors)
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
status = form.save(commit=False)
|
status = form.save(commit=False)
|
||||||
|
status.raise_not_editable(request.user)
|
||||||
# save the plain, unformatted version of the status for future editing
|
# save the plain, unformatted version of the status for future editing
|
||||||
status.raw_content = status.content
|
status.raw_content = status.content
|
||||||
if hasattr(status, "quote"):
|
if hasattr(status, "quote"):
|
||||||
|
@ -146,7 +147,7 @@ class DeleteStatus(View):
|
||||||
|
|
||||||
# perform deletion
|
# perform deletion
|
||||||
status.delete()
|
status.delete()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -195,7 +196,7 @@ def edit_readthrough(request):
|
||||||
|
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
def find_mentions(content):
|
def find_mentions(content):
|
||||||
|
|
|
@ -60,6 +60,12 @@ class User(View):
|
||||||
request.user,
|
request.user,
|
||||||
)
|
)
|
||||||
.filter(user=user)
|
.filter(user=user)
|
||||||
|
.exclude(
|
||||||
|
privacy="direct",
|
||||||
|
review__isnull=True,
|
||||||
|
comment__isnull=True,
|
||||||
|
quotation__isnull=True,
|
||||||
|
)
|
||||||
.select_related(
|
.select_related(
|
||||||
"user",
|
"user",
|
||||||
"reply_parent",
|
"reply_parent",
|
||||||
|
@ -158,10 +164,19 @@ def hide_suggestions(request):
|
||||||
"""not everyone wants user suggestions"""
|
"""not everyone wants user suggestions"""
|
||||||
request.user.show_suggested_users = False
|
request.user.show_suggested_users = False
|
||||||
request.user.save(broadcast=False, update_fields=["show_suggested_users"])
|
request.user.save(broadcast=False, update_fields=["show_suggested_users"])
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def user_redirect(request, username):
|
def user_redirect(request, username):
|
||||||
"""redirect to a user's feed"""
|
"""redirect to a user's feed"""
|
||||||
return redirect("user-feed", username=username)
|
return redirect("user-feed", username=username)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def toggle_guided_tour(request, tour):
|
||||||
|
"""most people don't want a tour every time they load a page"""
|
||||||
|
|
||||||
|
request.user.show_guided_tour = tour
|
||||||
|
request.user.save(broadcast=False, update_fields=["show_guided_tour"])
|
||||||
|
return redirect("/")
|
||||||
|
|
26
bw-dev
26
bw-dev
|
@ -3,6 +3,17 @@
|
||||||
# exit on errors
|
# exit on errors
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# check if we're in DEBUG mode
|
||||||
|
DEBUG=$(sed <.env -ne 's/^DEBUG=//p')
|
||||||
|
|
||||||
|
# disallow certain commands when debug is false
|
||||||
|
function prod_error {
|
||||||
|
if [ "$DEBUG" != "true" ]; then
|
||||||
|
echo "This command is not safe to run in production environments"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# import our ENV variables
|
# import our ENV variables
|
||||||
# catch exits and give a friendly error message
|
# catch exits and give a friendly error message
|
||||||
function showerr {
|
function showerr {
|
||||||
|
@ -65,12 +76,14 @@ case "$CMD" in
|
||||||
docker-compose up --build "$@"
|
docker-compose up --build "$@"
|
||||||
;;
|
;;
|
||||||
service_ports_web)
|
service_ports_web)
|
||||||
|
prod_error
|
||||||
docker-compose run --rm --service-ports web
|
docker-compose run --rm --service-ports web
|
||||||
;;
|
;;
|
||||||
initdb)
|
initdb)
|
||||||
initdb "@"
|
initdb "@"
|
||||||
;;
|
;;
|
||||||
resetdb)
|
resetdb)
|
||||||
|
prod_error
|
||||||
clean
|
clean
|
||||||
# Start just the DB so no one else is using it
|
# Start just the DB so no one else is using it
|
||||||
docker-compose up --build -d db
|
docker-compose up --build -d db
|
||||||
|
@ -83,6 +96,7 @@ case "$CMD" in
|
||||||
clean
|
clean
|
||||||
;;
|
;;
|
||||||
makemigrations)
|
makemigrations)
|
||||||
|
prod_error
|
||||||
runweb python manage.py makemigrations "$@"
|
runweb python manage.py makemigrations "$@"
|
||||||
;;
|
;;
|
||||||
migrate)
|
migrate)
|
||||||
|
@ -101,22 +115,27 @@ case "$CMD" in
|
||||||
docker-compose restart celery_worker
|
docker-compose restart celery_worker
|
||||||
;;
|
;;
|
||||||
pytest)
|
pytest)
|
||||||
|
prod_error
|
||||||
runweb pytest --no-cov-on-fail "$@"
|
runweb pytest --no-cov-on-fail "$@"
|
||||||
;;
|
;;
|
||||||
pytest_coverage_report)
|
pytest_coverage_report)
|
||||||
|
prod_error
|
||||||
runweb pytest -n 3 --cov-report term-missing "$@"
|
runweb pytest -n 3 --cov-report term-missing "$@"
|
||||||
;;
|
;;
|
||||||
collectstatic)
|
collectstatic)
|
||||||
runweb python manage.py collectstatic --no-input
|
runweb python manage.py collectstatic --no-input
|
||||||
;;
|
;;
|
||||||
makemessages)
|
makemessages)
|
||||||
|
prod_error
|
||||||
runweb django-admin makemessages --no-wrap --ignore=venv -l en_US $@
|
runweb django-admin makemessages --no-wrap --ignore=venv -l en_US $@
|
||||||
;;
|
;;
|
||||||
compilemessages)
|
compilemessages)
|
||||||
runweb django-admin compilemessages --ignore venv $@
|
runweb django-admin compilemessages --ignore venv $@
|
||||||
;;
|
;;
|
||||||
update_locales)
|
update_locales)
|
||||||
|
prod_error
|
||||||
git fetch origin l10n_main:l10n_main
|
git fetch origin l10n_main:l10n_main
|
||||||
|
git checkout l10n_main locale/ca_ES
|
||||||
git checkout l10n_main locale/de_DE
|
git checkout l10n_main locale/de_DE
|
||||||
git checkout l10n_main locale/es_ES
|
git checkout l10n_main locale/es_ES
|
||||||
git checkout l10n_main locale/fi_FI
|
git checkout l10n_main locale/fi_FI
|
||||||
|
@ -138,24 +157,30 @@ case "$CMD" in
|
||||||
docker-compose build
|
docker-compose build
|
||||||
;;
|
;;
|
||||||
clean)
|
clean)
|
||||||
|
prod_error
|
||||||
clean
|
clean
|
||||||
;;
|
;;
|
||||||
black)
|
black)
|
||||||
|
prod_error
|
||||||
docker-compose run --rm dev-tools black celerywyrm bookwyrm
|
docker-compose run --rm dev-tools black celerywyrm bookwyrm
|
||||||
;;
|
;;
|
||||||
pylint)
|
pylint)
|
||||||
|
prod_error
|
||||||
# pylint depends on having the app dependencies in place, so we run it in the web container
|
# pylint depends on having the app dependencies in place, so we run it in the web container
|
||||||
runweb pylint bookwyrm/
|
runweb pylint bookwyrm/
|
||||||
;;
|
;;
|
||||||
prettier)
|
prettier)
|
||||||
|
prod_error
|
||||||
docker-compose run --rm dev-tools npx prettier --write bookwyrm/static/js/*.js
|
docker-compose run --rm dev-tools npx prettier --write bookwyrm/static/js/*.js
|
||||||
;;
|
;;
|
||||||
stylelint)
|
stylelint)
|
||||||
|
prod_error
|
||||||
docker-compose run --rm dev-tools npx stylelint \
|
docker-compose run --rm dev-tools npx stylelint \
|
||||||
bookwyrm/static/css/bookwyrm.scss bookwyrm/static/css/bookwyrm/**/*.scss --fix \
|
bookwyrm/static/css/bookwyrm.scss bookwyrm/static/css/bookwyrm/**/*.scss --fix \
|
||||||
--config dev-tools/.stylelintrc.js
|
--config dev-tools/.stylelintrc.js
|
||||||
;;
|
;;
|
||||||
formatters)
|
formatters)
|
||||||
|
prod_error
|
||||||
runweb pylint bookwyrm/
|
runweb pylint bookwyrm/
|
||||||
docker-compose run --rm dev-tools black celerywyrm bookwyrm
|
docker-compose run --rm dev-tools black celerywyrm bookwyrm
|
||||||
docker-compose run --rm dev-tools npx prettier --write bookwyrm/static/js/*.js
|
docker-compose run --rm dev-tools npx prettier --write bookwyrm/static/js/*.js
|
||||||
|
@ -168,6 +193,7 @@ case "$CMD" in
|
||||||
runweb python manage.py collectstatic --no-input
|
runweb python manage.py collectstatic --no-input
|
||||||
;;
|
;;
|
||||||
collectstatic_watch)
|
collectstatic_watch)
|
||||||
|
prod_error
|
||||||
npm run --prefix dev-tools watch:static
|
npm run --prefix dev-tools watch:static
|
||||||
;;
|
;;
|
||||||
update)
|
update)
|
||||||
|
|
5391
locale/ca_ES/LC_MESSAGES/django.po
Normal file
5391
locale/ca_ES/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
@ -2,8 +2,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: bookwyrm\n"
|
"Project-Id-Version: bookwyrm\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-07-07 17:47+0000\n"
|
"POT-Creation-Date: 2022-07-15 19:29+0000\n"
|
||||||
"PO-Revision-Date: 2022-07-07 18:12\n"
|
"PO-Revision-Date: 2022-07-15 19:48\n"
|
||||||
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
|
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
|
||||||
"Language-Team: Spanish\n"
|
"Language-Team: Spanish\n"
|
||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
|
@ -42,19 +42,27 @@ msgstr "{i} usos"
|
||||||
msgid "Unlimited"
|
msgid "Unlimited"
|
||||||
msgstr "Sin límite"
|
msgstr "Sin límite"
|
||||||
|
|
||||||
|
#: bookwyrm/forms/edit_user.py:89
|
||||||
|
msgid "Incorrect password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/forms/edit_user.py:96 bookwyrm/forms/landing.py:71
|
||||||
|
msgid "Password does not match"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: bookwyrm/forms/forms.py:54
|
#: bookwyrm/forms/forms.py:54
|
||||||
msgid "Reading finish date cannot be before start date."
|
msgid "Reading finish date cannot be before start date."
|
||||||
msgstr "La fecha final de lectura no puede ser anterior a la fecha de inicio."
|
msgstr "La fecha final de lectura no puede ser anterior a la fecha de inicio."
|
||||||
|
|
||||||
#: bookwyrm/forms/forms.py:59
|
#: bookwyrm/forms/forms.py:59
|
||||||
msgid "Reading stopped date cannot be before start date."
|
msgid "Reading stopped date cannot be before start date."
|
||||||
msgstr ""
|
msgstr "La fecha final de lectura no puede ser anterior a la fecha de inicio."
|
||||||
|
|
||||||
#: bookwyrm/forms/landing.py:32
|
#: bookwyrm/forms/landing.py:38
|
||||||
msgid "User with this username already exists"
|
msgid "User with this username already exists"
|
||||||
msgstr "Este nombre de usuario ya está en uso."
|
msgstr "Este nombre de usuario ya está en uso."
|
||||||
|
|
||||||
#: bookwyrm/forms/landing.py:41
|
#: bookwyrm/forms/landing.py:47
|
||||||
msgid "A user with this email already exists."
|
msgid "A user with this email already exists."
|
||||||
msgstr "Ya existe un usuario con ese correo electrónico."
|
msgstr "Ya existe un usuario con ese correo electrónico."
|
||||||
|
|
||||||
|
@ -288,58 +296,62 @@ msgid "English"
|
||||||
msgstr "English (Inglés)"
|
msgstr "English (Inglés)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:283
|
#: bookwyrm/settings.py:283
|
||||||
|
msgid "Català (Catalan)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/settings.py:284
|
||||||
msgid "Deutsch (German)"
|
msgid "Deutsch (German)"
|
||||||
msgstr "Deutsch (Alemán)"
|
msgstr "Deutsch (Alemán)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:284
|
#: bookwyrm/settings.py:285
|
||||||
msgid "Español (Spanish)"
|
msgid "Español (Spanish)"
|
||||||
msgstr "Español"
|
msgstr "Español"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:285
|
#: bookwyrm/settings.py:286
|
||||||
msgid "Galego (Galician)"
|
msgid "Galego (Galician)"
|
||||||
msgstr "Galego (gallego)"
|
msgstr "Galego (gallego)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:286
|
#: bookwyrm/settings.py:287
|
||||||
msgid "Italiano (Italian)"
|
msgid "Italiano (Italian)"
|
||||||
msgstr "Italiano"
|
msgstr "Italiano"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:287
|
#: bookwyrm/settings.py:288
|
||||||
msgid "Suomi (Finnish)"
|
msgid "Suomi (Finnish)"
|
||||||
msgstr "Suomi (finés)"
|
msgstr "Suomi (finés)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:288
|
#: bookwyrm/settings.py:289
|
||||||
msgid "Français (French)"
|
msgid "Français (French)"
|
||||||
msgstr "Français (Francés)"
|
msgstr "Français (Francés)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:289
|
#: bookwyrm/settings.py:290
|
||||||
msgid "Lietuvių (Lithuanian)"
|
msgid "Lietuvių (Lithuanian)"
|
||||||
msgstr "Lietuvių (Lituano)"
|
msgstr "Lietuvių (Lituano)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:290
|
#: bookwyrm/settings.py:291
|
||||||
msgid "Norsk (Norwegian)"
|
msgid "Norsk (Norwegian)"
|
||||||
msgstr "Norsk (noruego)"
|
msgstr "Norsk (noruego)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:291
|
#: bookwyrm/settings.py:292
|
||||||
msgid "Português do Brasil (Brazilian Portuguese)"
|
msgid "Português do Brasil (Brazilian Portuguese)"
|
||||||
msgstr "Português do Brasil (portugués brasileño)"
|
msgstr "Português do Brasil (portugués brasileño)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:292
|
#: bookwyrm/settings.py:293
|
||||||
msgid "Português Europeu (European Portuguese)"
|
msgid "Português Europeu (European Portuguese)"
|
||||||
msgstr "Português Europeu (Portugués europeo)"
|
msgstr "Português Europeu (Portugués europeo)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:293
|
#: bookwyrm/settings.py:294
|
||||||
msgid "Română (Romanian)"
|
msgid "Română (Romanian)"
|
||||||
msgstr "Română (rumano)"
|
msgstr "Română (rumano)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:294
|
#: bookwyrm/settings.py:295
|
||||||
msgid "Svenska (Swedish)"
|
msgid "Svenska (Swedish)"
|
||||||
msgstr "Svenska (Sueco)"
|
msgstr "Svenska (Sueco)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:295
|
#: bookwyrm/settings.py:296
|
||||||
msgid "简体中文 (Simplified Chinese)"
|
msgid "简体中文 (Simplified Chinese)"
|
||||||
msgstr "简体中文 (Chino simplificado)"
|
msgstr "简体中文 (Chino simplificado)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:296
|
#: bookwyrm/settings.py:297
|
||||||
msgid "繁體中文 (Traditional Chinese)"
|
msgid "繁體中文 (Traditional Chinese)"
|
||||||
msgstr "繁體中文 (Chino tradicional)"
|
msgstr "繁體中文 (Chino tradicional)"
|
||||||
|
|
||||||
|
@ -787,7 +799,7 @@ msgstr "La carga de datos se conectará a <strong>%(source_name)s</strong> y com
|
||||||
#: bookwyrm/templates/book/edit/edit_book.html:122
|
#: bookwyrm/templates/book/edit/edit_book.html:122
|
||||||
#: bookwyrm/templates/book/sync_modal.html:24
|
#: bookwyrm/templates/book/sync_modal.html:24
|
||||||
#: bookwyrm/templates/groups/members.html:29
|
#: bookwyrm/templates/groups/members.html:29
|
||||||
#: bookwyrm/templates/landing/password_reset.html:42
|
#: bookwyrm/templates/landing/password_reset.html:52
|
||||||
#: bookwyrm/templates/snippets/remove_from_group_button.html:17
|
#: bookwyrm/templates/snippets/remove_from_group_button.html:17
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Confirmar"
|
msgstr "Confirmar"
|
||||||
|
@ -1205,7 +1217,7 @@ msgstr "Dominio"
|
||||||
#: bookwyrm/templates/settings/announcements/announcements.html:37
|
#: bookwyrm/templates/settings/announcements/announcements.html:37
|
||||||
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:47
|
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:47
|
||||||
#: bookwyrm/templates/settings/invites/status_filter.html:5
|
#: bookwyrm/templates/settings/invites/status_filter.html:5
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:52
|
#: bookwyrm/templates/settings/users/user_admin.html:56
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:24
|
#: bookwyrm/templates/settings/users/user_info.html:24
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Estado"
|
msgstr "Estado"
|
||||||
|
@ -1221,7 +1233,7 @@ msgstr "Acciones"
|
||||||
#: bookwyrm/templates/book/file_links/edit_links.html:48
|
#: bookwyrm/templates/book/file_links/edit_links.html:48
|
||||||
#: bookwyrm/templates/settings/link_domains/link_table.html:21
|
#: bookwyrm/templates/settings/link_domains/link_table.html:21
|
||||||
msgid "Unknown user"
|
msgid "Unknown user"
|
||||||
msgstr ""
|
msgstr "Usuario/a desconocido/a"
|
||||||
|
|
||||||
#: bookwyrm/templates/book/file_links/edit_links.html:57
|
#: bookwyrm/templates/book/file_links/edit_links.html:57
|
||||||
#: bookwyrm/templates/book/file_links/verification_modal.html:22
|
#: bookwyrm/templates/book/file_links/verification_modal.html:22
|
||||||
|
@ -1329,7 +1341,7 @@ msgstr "Código de confirmación:"
|
||||||
|
|
||||||
#: bookwyrm/templates/confirm_email/confirm_email.html:25
|
#: bookwyrm/templates/confirm_email/confirm_email.html:25
|
||||||
#: bookwyrm/templates/landing/layout.html:81
|
#: bookwyrm/templates/landing/layout.html:81
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:127
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:106
|
||||||
#: bookwyrm/templates/snippets/report_modal.html:53
|
#: bookwyrm/templates/snippets/report_modal.html:53
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr "Enviar"
|
msgstr "Enviar"
|
||||||
|
@ -1351,11 +1363,7 @@ msgstr "Reenviar enlace de confirmación"
|
||||||
msgid "Email address:"
|
msgid "Email address:"
|
||||||
msgstr "Dirección de correo electrónico:"
|
msgstr "Dirección de correo electrónico:"
|
||||||
|
|
||||||
#: bookwyrm/templates/confirm_email/resend_modal.html:28
|
#: bookwyrm/templates/confirm_email/resend_modal.html:30
|
||||||
msgid "No user matching this email address found."
|
|
||||||
msgstr "No hay usuarios con esta dirección de correo electrónico."
|
|
||||||
|
|
||||||
#: bookwyrm/templates/confirm_email/resend_modal.html:38
|
|
||||||
msgid "Resend link"
|
msgid "Resend link"
|
||||||
msgstr "Re-enviar enlace"
|
msgstr "Re-enviar enlace"
|
||||||
|
|
||||||
|
@ -1369,7 +1377,7 @@ msgid "Local users"
|
||||||
msgstr "Usuarios locales"
|
msgstr "Usuarios locales"
|
||||||
|
|
||||||
#: bookwyrm/templates/directory/community_filter.html:12
|
#: bookwyrm/templates/directory/community_filter.html:12
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:29
|
#: bookwyrm/templates/settings/users/user_admin.html:33
|
||||||
msgid "Federated community"
|
msgid "Federated community"
|
||||||
msgstr "Comunidad federada"
|
msgstr "Comunidad federada"
|
||||||
|
|
||||||
|
@ -1746,7 +1754,7 @@ msgstr "Leído"
|
||||||
#: bookwyrm/templates/get_started/book_preview.html:13
|
#: bookwyrm/templates/get_started/book_preview.html:13
|
||||||
#: bookwyrm/templates/shelf/shelf.html:89 bookwyrm/templates/user/user.html:36
|
#: bookwyrm/templates/shelf/shelf.html:89 bookwyrm/templates/user/user.html:36
|
||||||
msgid "Stopped Reading"
|
msgid "Stopped Reading"
|
||||||
msgstr ""
|
msgstr "Lectura interrumpida"
|
||||||
|
|
||||||
#: bookwyrm/templates/get_started/books.html:6
|
#: bookwyrm/templates/get_started/books.html:6
|
||||||
msgid "What are you reading?"
|
msgid "What are you reading?"
|
||||||
|
@ -2272,8 +2280,8 @@ msgstr "¿Olvidaste tu contraseña?"
|
||||||
msgid "More about this site"
|
msgid "More about this site"
|
||||||
msgstr "Más sobre este sitio"
|
msgstr "Más sobre este sitio"
|
||||||
|
|
||||||
#: bookwyrm/templates/landing/password_reset.html:34
|
#: bookwyrm/templates/landing/password_reset.html:43
|
||||||
#: bookwyrm/templates/preferences/change_password.html:18
|
#: bookwyrm/templates/preferences/change_password.html:33
|
||||||
#: bookwyrm/templates/preferences/delete_user.html:20
|
#: bookwyrm/templates/preferences/delete_user.html:20
|
||||||
msgid "Confirm password:"
|
msgid "Confirm password:"
|
||||||
msgstr "Confirmar contraseña:"
|
msgstr "Confirmar contraseña:"
|
||||||
|
@ -2281,7 +2289,7 @@ msgstr "Confirmar contraseña:"
|
||||||
#: bookwyrm/templates/landing/password_reset_request.html:14
|
#: bookwyrm/templates/landing/password_reset_request.html:14
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "A password reset link will be sent to <strong>%(email)s</strong> if there is an account using that email address."
|
msgid "A password reset link will be sent to <strong>%(email)s</strong> if there is an account using that email address."
|
||||||
msgstr ""
|
msgstr "Se enviará un enlace para restablecer la contraseña a <strong>%(email)s</strong> si hay una cuenta usando esa dirección de correo electrónico."
|
||||||
|
|
||||||
#: bookwyrm/templates/landing/password_reset_request.html:20
|
#: bookwyrm/templates/landing/password_reset_request.html:20
|
||||||
msgid "A link to reset your password will be sent to your email address"
|
msgid "A link to reset your password will be sent to your email address"
|
||||||
|
@ -2598,7 +2606,7 @@ msgstr "Listas guardadas"
|
||||||
#: bookwyrm/templates/notifications/items/accept.html:18
|
#: bookwyrm/templates/notifications/items/accept.html:18
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> aceptó su invitación para unirse al grupo \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/accept.html:26
|
#: bookwyrm/templates/notifications/items/accept.html:26
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -2871,6 +2879,11 @@ msgid_plural "%(display_count)s new <a href=\"%(path)s\">reports</a> need modera
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/notifications/items/status_preview.html:4
|
||||||
|
#: bookwyrm/templates/snippets/status/content_status.html:73
|
||||||
|
msgid "Content warning"
|
||||||
|
msgstr "Advertencia de contenido"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/update.html:16
|
#: bookwyrm/templates/notifications/items/update.html:16
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "has changed the privacy level for <a href=\"%(group_path)s\">%(group_name)s</a>"
|
msgid "has changed the privacy level for <a href=\"%(group_path)s\">%(group_name)s</a>"
|
||||||
|
@ -3028,12 +3041,20 @@ msgstr "No hay ningún usuario bloqueado actualmente."
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/change_password.html:4
|
#: bookwyrm/templates/preferences/change_password.html:4
|
||||||
#: bookwyrm/templates/preferences/change_password.html:7
|
#: bookwyrm/templates/preferences/change_password.html:7
|
||||||
#: bookwyrm/templates/preferences/change_password.html:21
|
#: bookwyrm/templates/preferences/change_password.html:37
|
||||||
#: bookwyrm/templates/preferences/layout.html:20
|
#: bookwyrm/templates/preferences/layout.html:20
|
||||||
msgid "Change Password"
|
msgid "Change Password"
|
||||||
msgstr "Cambiar contraseña"
|
msgstr "Cambiar contraseña"
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/change_password.html:14
|
#: bookwyrm/templates/preferences/change_password.html:15
|
||||||
|
msgid "Successfully changed password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/preferences/change_password.html:22
|
||||||
|
msgid "Current password:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/preferences/change_password.html:28
|
||||||
msgid "New password:"
|
msgid "New password:"
|
||||||
msgstr "Nueva contraseña:"
|
msgstr "Nueva contraseña:"
|
||||||
|
|
||||||
|
@ -3125,6 +3146,10 @@ msgstr "Exportar CSV"
|
||||||
msgid "Your export will include all the books on your shelves, books you have reviewed, and books with reading activity."
|
msgid "Your export will include all the books on your shelves, books you have reviewed, and books with reading activity."
|
||||||
msgstr "Se exportarán todos los libros que tengas en las estanterías, las reseñas y los libros que estés leyendo."
|
msgstr "Se exportarán todos los libros que tengas en las estanterías, las reseñas y los libros que estés leyendo."
|
||||||
|
|
||||||
|
#: bookwyrm/templates/preferences/export.html:20
|
||||||
|
msgid "Download file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/layout.html:11
|
#: bookwyrm/templates/preferences/layout.html:11
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Cuenta"
|
msgstr "Cuenta"
|
||||||
|
@ -3353,13 +3378,13 @@ msgstr "Falso"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/announcements/announcement.html:57
|
#: bookwyrm/templates/settings/announcements/announcement.html:57
|
||||||
#: bookwyrm/templates/settings/announcements/edit_announcement.html:79
|
#: bookwyrm/templates/settings/announcements/edit_announcement.html:79
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:105
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:84
|
||||||
msgid "Start date:"
|
msgid "Start date:"
|
||||||
msgstr "Fecha de inicio:"
|
msgstr "Fecha de inicio:"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/announcements/announcement.html:62
|
#: bookwyrm/templates/settings/announcements/announcement.html:62
|
||||||
#: bookwyrm/templates/settings/announcements/edit_announcement.html:89
|
#: bookwyrm/templates/settings/announcements/edit_announcement.html:89
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:111
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:90
|
||||||
msgid "End date:"
|
msgid "End date:"
|
||||||
msgstr "Fecha final:"
|
msgstr "Fecha final:"
|
||||||
|
|
||||||
|
@ -3519,7 +3544,7 @@ msgid "Dashboard"
|
||||||
msgstr "Tablero"
|
msgstr "Tablero"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:15
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:15
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:134
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:113
|
||||||
msgid "Total users"
|
msgid "Total users"
|
||||||
msgstr "Número de usuarios"
|
msgstr "Número de usuarios"
|
||||||
|
|
||||||
|
@ -3537,66 +3562,31 @@ msgstr "Estados"
|
||||||
msgid "Works"
|
msgid "Works"
|
||||||
msgstr "Obras"
|
msgstr "Obras"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:43
|
|
||||||
#, python-format
|
|
||||||
msgid "Your outgoing email address, <code>%(email_sender)s</code>, may be misconfigured."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:46
|
|
||||||
msgid "Check the <code>EMAIL_SENDER_NAME</code> and <code>EMAIL_SENDER_DOMAIN</code> in your <code>.env</code>."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:54
|
|
||||||
#, python-format
|
|
||||||
msgid "%(display_count)s open report"
|
|
||||||
msgid_plural "%(display_count)s open reports"
|
|
||||||
msgstr[0] "%(display_count)s informe abierto"
|
|
||||||
msgstr[1] "%(display_count)s informes abiertos"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:66
|
|
||||||
#, python-format
|
|
||||||
msgid "%(display_count)s domain needs review"
|
|
||||||
msgid_plural "%(display_count)s domains need review"
|
|
||||||
msgstr[0] "%(display_count)s dominio necesita revisión"
|
|
||||||
msgstr[1] "%(display_count)s dominios necesitan revisión"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:78
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:78
|
||||||
#, python-format
|
|
||||||
msgid "%(display_count)s invite request"
|
|
||||||
msgid_plural "%(display_count)s invite requests"
|
|
||||||
msgstr[0] "%(display_count)s solicitación de invitado"
|
|
||||||
msgstr[1] "%(display_count)s solicitaciones de invitado"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:90
|
|
||||||
#, python-format
|
|
||||||
msgid "An update is available! You're running v%(current)s and the latest release is %(available)s."
|
|
||||||
msgstr "Hay una actualización disponible. La versión que estás usando es la %(current)s, mientras que la actual es %(available)s."
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:99
|
|
||||||
msgid "Instance Activity"
|
msgid "Instance Activity"
|
||||||
msgstr "Actividad de instancia"
|
msgstr "Actividad de instancia"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:117
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:96
|
||||||
msgid "Interval:"
|
msgid "Interval:"
|
||||||
msgstr "Intervalo:"
|
msgstr "Intervalo:"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:121
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:100
|
||||||
msgid "Days"
|
msgid "Days"
|
||||||
msgstr "Dias"
|
msgstr "Dias"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:122
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:101
|
||||||
msgid "Weeks"
|
msgid "Weeks"
|
||||||
msgstr "Semanas"
|
msgstr "Semanas"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:140
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:119
|
||||||
msgid "User signup activity"
|
msgid "User signup activity"
|
||||||
msgstr "Actividad de inscripciones de usuarios"
|
msgstr "Actividad de inscripciones de usuarios"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:146
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:125
|
||||||
msgid "Status activity"
|
msgid "Status activity"
|
||||||
msgstr "Actividad de estado"
|
msgstr "Actividad de estado"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:152
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:131
|
||||||
msgid "Works created"
|
msgid "Works created"
|
||||||
msgstr "Obras creadas"
|
msgstr "Obras creadas"
|
||||||
|
|
||||||
|
@ -3612,6 +3602,49 @@ msgstr "Estados publicados"
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Suma"
|
msgstr "Suma"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/domain_review.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid "%(display_count)s domain needs review"
|
||||||
|
msgid_plural "%(display_count)s domains need review"
|
||||||
|
msgstr[0] "%(display_count)s dominio necesita revisión"
|
||||||
|
msgstr[1] "%(display_count)s dominios necesitan revisión"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/email_config.html:8
|
||||||
|
#, python-format
|
||||||
|
msgid "Your outgoing email address, <code>%(email_sender)s</code>, may be misconfigured."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/email_config.html:11
|
||||||
|
msgid "Check the <code>EMAIL_SENDER_NAME</code> and <code>EMAIL_SENDER_DOMAIN</code> in your <code>.env</code> file."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/invites.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid "%(display_count)s invite request"
|
||||||
|
msgid_plural "%(display_count)s invite requests"
|
||||||
|
msgstr[0] "%(display_count)s solicitación de invitado"
|
||||||
|
msgstr[1] "%(display_count)s solicitaciones de invitado"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/missing_conduct.html:8
|
||||||
|
msgid "Your instance is missing a code of conduct."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/missing_privacy.html:8
|
||||||
|
msgid "Your instance is missing a privacy policy."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/reports.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid "%(display_count)s open report"
|
||||||
|
msgid_plural "%(display_count)s open reports"
|
||||||
|
msgstr[0] "%(display_count)s informe abierto"
|
||||||
|
msgstr[1] "%(display_count)s informes abiertos"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/update_version.html:8
|
||||||
|
#, python-format
|
||||||
|
msgid "An update is available! You're running v%(current)s and the latest release is %(available)s."
|
||||||
|
msgstr "Hay una actualización disponible. La versión que estás usando es la %(current)s, mientras que la actual es %(available)s."
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/email_blocklist/domain_form.html:5
|
#: bookwyrm/templates/settings/email_blocklist/domain_form.html:5
|
||||||
#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10
|
#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10
|
||||||
msgid "Add domain"
|
msgid "Add domain"
|
||||||
|
@ -4308,38 +4341,42 @@ msgstr "Tu contraseña:"
|
||||||
msgid "Users: <small>%(instance_name)s</small>"
|
msgid "Users: <small>%(instance_name)s</small>"
|
||||||
msgstr "Usuarios <small>%(instance_name)s</small>"
|
msgstr "Usuarios <small>%(instance_name)s</small>"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:40
|
#: bookwyrm/templates/settings/users/user_admin.html:29
|
||||||
|
msgid "Deleted users"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/users/user_admin.html:44
|
||||||
#: bookwyrm/templates/settings/users/username_filter.html:5
|
#: bookwyrm/templates/settings/users/username_filter.html:5
|
||||||
msgid "Username"
|
msgid "Username"
|
||||||
msgstr "Nombre de usuario"
|
msgstr "Nombre de usuario"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:44
|
#: bookwyrm/templates/settings/users/user_admin.html:48
|
||||||
msgid "Date Added"
|
msgid "Date Added"
|
||||||
msgstr "Fecha agregada"
|
msgstr "Fecha agregada"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:48
|
#: bookwyrm/templates/settings/users/user_admin.html:52
|
||||||
msgid "Last Active"
|
msgid "Last Active"
|
||||||
msgstr "Actividad reciente"
|
msgstr "Actividad reciente"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:57
|
#: bookwyrm/templates/settings/users/user_admin.html:61
|
||||||
msgid "Remote instance"
|
msgid "Remote instance"
|
||||||
msgstr "Instancia remota"
|
msgstr "Instancia remota"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:74
|
#: bookwyrm/templates/settings/users/user_admin.html:81
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:28
|
#: bookwyrm/templates/settings/users/user_info.html:28
|
||||||
msgid "Active"
|
msgid "Active"
|
||||||
msgstr "Activo"
|
msgstr "Activo"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:79
|
#: bookwyrm/templates/settings/users/user_admin.html:86
|
||||||
msgid "Deleted"
|
msgid "Deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:85
|
#: bookwyrm/templates/settings/users/user_admin.html:92
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:32
|
#: bookwyrm/templates/settings/users/user_info.html:32
|
||||||
msgid "Inactive"
|
msgid "Inactive"
|
||||||
msgstr "Inactivo"
|
msgstr "Inactivo"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:94
|
#: bookwyrm/templates/settings/users/user_admin.html:101
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:127
|
#: bookwyrm/templates/settings/users/user_info.html:127
|
||||||
msgid "Not set"
|
msgid "Not set"
|
||||||
msgstr "No establecido"
|
msgstr "No establecido"
|
||||||
|
@ -5024,10 +5061,6 @@ msgstr ""
|
||||||
msgid "Finish reading"
|
msgid "Finish reading"
|
||||||
msgstr "Terminar de leer"
|
msgstr "Terminar de leer"
|
||||||
|
|
||||||
#: bookwyrm/templates/snippets/status/content_status.html:73
|
|
||||||
msgid "Content warning"
|
|
||||||
msgstr "Advertencia de contenido"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/snippets/status/content_status.html:80
|
#: bookwyrm/templates/snippets/status/content_status.html:80
|
||||||
msgid "Show status"
|
msgid "Show status"
|
||||||
msgstr "Mostrar estado"
|
msgstr "Mostrar estado"
|
||||||
|
@ -5323,7 +5356,7 @@ msgstr "No le sigue nadie que tu sigas"
|
||||||
msgid "View profile and more"
|
msgid "View profile and more"
|
||||||
msgstr "Ver perfil y más"
|
msgstr "Ver perfil y más"
|
||||||
|
|
||||||
#: bookwyrm/templates/user_menu.html:72
|
#: bookwyrm/templates/user_menu.html:78
|
||||||
msgid "Log out"
|
msgid "Log out"
|
||||||
msgstr "Cerrar sesión"
|
msgstr "Cerrar sesión"
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -2,8 +2,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: bookwyrm\n"
|
"Project-Id-Version: bookwyrm\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-07-07 17:47+0000\n"
|
"POT-Creation-Date: 2022-07-15 19:29+0000\n"
|
||||||
"PO-Revision-Date: 2022-07-07 18:12\n"
|
"PO-Revision-Date: 2022-07-22 17:47\n"
|
||||||
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
|
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
|
||||||
"Language-Team: Finnish\n"
|
"Language-Team: Finnish\n"
|
||||||
"Language: fi\n"
|
"Language: fi\n"
|
||||||
|
@ -42,6 +42,14 @@ msgstr "{i} käyttökertaa"
|
||||||
msgid "Unlimited"
|
msgid "Unlimited"
|
||||||
msgstr "rajattomasti"
|
msgstr "rajattomasti"
|
||||||
|
|
||||||
|
#: bookwyrm/forms/edit_user.py:89
|
||||||
|
msgid "Incorrect password"
|
||||||
|
msgstr "Väärä salasana"
|
||||||
|
|
||||||
|
#: bookwyrm/forms/edit_user.py:96 bookwyrm/forms/landing.py:71
|
||||||
|
msgid "Password does not match"
|
||||||
|
msgstr "Salasanat eivät täsmää"
|
||||||
|
|
||||||
#: bookwyrm/forms/forms.py:54
|
#: bookwyrm/forms/forms.py:54
|
||||||
msgid "Reading finish date cannot be before start date."
|
msgid "Reading finish date cannot be before start date."
|
||||||
msgstr "Lopetuspäivä ei voi olla ennen aloituspäivää."
|
msgstr "Lopetuspäivä ei voi olla ennen aloituspäivää."
|
||||||
|
@ -50,11 +58,11 @@ msgstr "Lopetuspäivä ei voi olla ennen aloituspäivää."
|
||||||
msgid "Reading stopped date cannot be before start date."
|
msgid "Reading stopped date cannot be before start date."
|
||||||
msgstr "Keskeytyspäivä ei voi olla ennen aloituspäivää."
|
msgstr "Keskeytyspäivä ei voi olla ennen aloituspäivää."
|
||||||
|
|
||||||
#: bookwyrm/forms/landing.py:32
|
#: bookwyrm/forms/landing.py:38
|
||||||
msgid "User with this username already exists"
|
msgid "User with this username already exists"
|
||||||
msgstr "Käyttäjänimi on jo varattu"
|
msgstr "Käyttäjänimi on jo varattu"
|
||||||
|
|
||||||
#: bookwyrm/forms/landing.py:41
|
#: bookwyrm/forms/landing.py:47
|
||||||
msgid "A user with this email already exists."
|
msgid "A user with this email already exists."
|
||||||
msgstr "Sähköpostiosoite on jo jonkun käyttäjän käytössä."
|
msgstr "Sähköpostiosoite on jo jonkun käyttäjän käytössä."
|
||||||
|
|
||||||
|
@ -288,58 +296,62 @@ msgid "English"
|
||||||
msgstr "English (englanti)"
|
msgstr "English (englanti)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:283
|
#: bookwyrm/settings.py:283
|
||||||
|
msgid "Català (Catalan)"
|
||||||
|
msgstr "Català (katalaani)"
|
||||||
|
|
||||||
|
#: bookwyrm/settings.py:284
|
||||||
msgid "Deutsch (German)"
|
msgid "Deutsch (German)"
|
||||||
msgstr "Deutsch (saksa)"
|
msgstr "Deutsch (saksa)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:284
|
#: bookwyrm/settings.py:285
|
||||||
msgid "Español (Spanish)"
|
msgid "Español (Spanish)"
|
||||||
msgstr "Español (espanja)"
|
msgstr "Español (espanja)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:285
|
#: bookwyrm/settings.py:286
|
||||||
msgid "Galego (Galician)"
|
msgid "Galego (Galician)"
|
||||||
msgstr "Galego (galego)"
|
msgstr "Galego (galego)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:286
|
#: bookwyrm/settings.py:287
|
||||||
msgid "Italiano (Italian)"
|
msgid "Italiano (Italian)"
|
||||||
msgstr "Italiano (italia)"
|
msgstr "Italiano (italia)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:287
|
#: bookwyrm/settings.py:288
|
||||||
msgid "Suomi (Finnish)"
|
msgid "Suomi (Finnish)"
|
||||||
msgstr "suomi"
|
msgstr "suomi"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:288
|
#: bookwyrm/settings.py:289
|
||||||
msgid "Français (French)"
|
msgid "Français (French)"
|
||||||
msgstr "Français (ranska)"
|
msgstr "Français (ranska)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:289
|
#: bookwyrm/settings.py:290
|
||||||
msgid "Lietuvių (Lithuanian)"
|
msgid "Lietuvių (Lithuanian)"
|
||||||
msgstr "Lietuvių (liettua)"
|
msgstr "Lietuvių (liettua)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:290
|
#: bookwyrm/settings.py:291
|
||||||
msgid "Norsk (Norwegian)"
|
msgid "Norsk (Norwegian)"
|
||||||
msgstr "Norsk (norja)"
|
msgstr "Norsk (norja)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:291
|
#: bookwyrm/settings.py:292
|
||||||
msgid "Português do Brasil (Brazilian Portuguese)"
|
msgid "Português do Brasil (Brazilian Portuguese)"
|
||||||
msgstr "Português do Brasil (brasilianportugali)"
|
msgstr "Português do Brasil (brasilianportugali)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:292
|
#: bookwyrm/settings.py:293
|
||||||
msgid "Português Europeu (European Portuguese)"
|
msgid "Português Europeu (European Portuguese)"
|
||||||
msgstr "Português Europeu (portugali)"
|
msgstr "Português Europeu (portugali)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:293
|
#: bookwyrm/settings.py:294
|
||||||
msgid "Română (Romanian)"
|
msgid "Română (Romanian)"
|
||||||
msgstr "Română (romania)"
|
msgstr "Română (romania)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:294
|
#: bookwyrm/settings.py:295
|
||||||
msgid "Svenska (Swedish)"
|
msgid "Svenska (Swedish)"
|
||||||
msgstr "Svenska (ruotsi)"
|
msgstr "Svenska (ruotsi)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:295
|
#: bookwyrm/settings.py:296
|
||||||
msgid "简体中文 (Simplified Chinese)"
|
msgid "简体中文 (Simplified Chinese)"
|
||||||
msgstr "简体中文 (yksinkertaistettu kiina)"
|
msgstr "简体中文 (yksinkertaistettu kiina)"
|
||||||
|
|
||||||
#: bookwyrm/settings.py:296
|
#: bookwyrm/settings.py:297
|
||||||
msgid "繁體中文 (Traditional Chinese)"
|
msgid "繁體中文 (Traditional Chinese)"
|
||||||
msgstr "繁體中文 (perinteinen kiina)"
|
msgstr "繁體中文 (perinteinen kiina)"
|
||||||
|
|
||||||
|
@ -787,7 +799,7 @@ msgstr "Tietoja ladattaessa muodostetaan yhteys lähteeseen <strong>%(source_nam
|
||||||
#: bookwyrm/templates/book/edit/edit_book.html:122
|
#: bookwyrm/templates/book/edit/edit_book.html:122
|
||||||
#: bookwyrm/templates/book/sync_modal.html:24
|
#: bookwyrm/templates/book/sync_modal.html:24
|
||||||
#: bookwyrm/templates/groups/members.html:29
|
#: bookwyrm/templates/groups/members.html:29
|
||||||
#: bookwyrm/templates/landing/password_reset.html:42
|
#: bookwyrm/templates/landing/password_reset.html:52
|
||||||
#: bookwyrm/templates/snippets/remove_from_group_button.html:17
|
#: bookwyrm/templates/snippets/remove_from_group_button.html:17
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Vahvista"
|
msgstr "Vahvista"
|
||||||
|
@ -1205,7 +1217,7 @@ msgstr "Verkkotunnus"
|
||||||
#: bookwyrm/templates/settings/announcements/announcements.html:37
|
#: bookwyrm/templates/settings/announcements/announcements.html:37
|
||||||
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:47
|
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:47
|
||||||
#: bookwyrm/templates/settings/invites/status_filter.html:5
|
#: bookwyrm/templates/settings/invites/status_filter.html:5
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:52
|
#: bookwyrm/templates/settings/users/user_admin.html:56
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:24
|
#: bookwyrm/templates/settings/users/user_info.html:24
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Tila"
|
msgstr "Tila"
|
||||||
|
@ -1221,7 +1233,7 @@ msgstr "Toiminnot"
|
||||||
#: bookwyrm/templates/book/file_links/edit_links.html:48
|
#: bookwyrm/templates/book/file_links/edit_links.html:48
|
||||||
#: bookwyrm/templates/settings/link_domains/link_table.html:21
|
#: bookwyrm/templates/settings/link_domains/link_table.html:21
|
||||||
msgid "Unknown user"
|
msgid "Unknown user"
|
||||||
msgstr ""
|
msgstr "Tuntematon käyttäjä"
|
||||||
|
|
||||||
#: bookwyrm/templates/book/file_links/edit_links.html:57
|
#: bookwyrm/templates/book/file_links/edit_links.html:57
|
||||||
#: bookwyrm/templates/book/file_links/verification_modal.html:22
|
#: bookwyrm/templates/book/file_links/verification_modal.html:22
|
||||||
|
@ -1329,7 +1341,7 @@ msgstr "Vahvistuskoodi:"
|
||||||
|
|
||||||
#: bookwyrm/templates/confirm_email/confirm_email.html:25
|
#: bookwyrm/templates/confirm_email/confirm_email.html:25
|
||||||
#: bookwyrm/templates/landing/layout.html:81
|
#: bookwyrm/templates/landing/layout.html:81
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:127
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:106
|
||||||
#: bookwyrm/templates/snippets/report_modal.html:53
|
#: bookwyrm/templates/snippets/report_modal.html:53
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr "Lähetä"
|
msgstr "Lähetä"
|
||||||
|
@ -1351,11 +1363,7 @@ msgstr "Lähetä vahvistuslinkki uudelleen"
|
||||||
msgid "Email address:"
|
msgid "Email address:"
|
||||||
msgstr "Sähköpostiosoite:"
|
msgstr "Sähköpostiosoite:"
|
||||||
|
|
||||||
#: bookwyrm/templates/confirm_email/resend_modal.html:28
|
#: bookwyrm/templates/confirm_email/resend_modal.html:30
|
||||||
msgid "No user matching this email address found."
|
|
||||||
msgstr "Tähän sähköpostiosoitteeseen ei ole yhdistetty käyttäjää."
|
|
||||||
|
|
||||||
#: bookwyrm/templates/confirm_email/resend_modal.html:38
|
|
||||||
msgid "Resend link"
|
msgid "Resend link"
|
||||||
msgstr "Lähetä linkki uudelleen"
|
msgstr "Lähetä linkki uudelleen"
|
||||||
|
|
||||||
|
@ -1369,7 +1377,7 @@ msgid "Local users"
|
||||||
msgstr "Paikalliset käyttäjät"
|
msgstr "Paikalliset käyttäjät"
|
||||||
|
|
||||||
#: bookwyrm/templates/directory/community_filter.html:12
|
#: bookwyrm/templates/directory/community_filter.html:12
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:29
|
#: bookwyrm/templates/settings/users/user_admin.html:33
|
||||||
msgid "Federated community"
|
msgid "Federated community"
|
||||||
msgstr "Yhteisö fediversumissa"
|
msgstr "Yhteisö fediversumissa"
|
||||||
|
|
||||||
|
@ -1576,13 +1584,13 @@ msgstr "Lue lisää %(site_name)s-yhteisöstä:"
|
||||||
#: bookwyrm/templates/email/moderation_report/text_content.html:6
|
#: bookwyrm/templates/email/moderation_report/text_content.html:6
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "@%(reporter)s has flagged a link domain for moderation."
|
msgid "@%(reporter)s has flagged a link domain for moderation."
|
||||||
msgstr ""
|
msgstr "@%(reporter)s on merkinnyt verkkotunnuksen tarkastettavaksi."
|
||||||
|
|
||||||
#: bookwyrm/templates/email/moderation_report/html_content.html:14
|
#: bookwyrm/templates/email/moderation_report/html_content.html:14
|
||||||
#: bookwyrm/templates/email/moderation_report/text_content.html:10
|
#: bookwyrm/templates/email/moderation_report/text_content.html:10
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "@%(reporter)s has flagged behavior by @%(reportee)s for moderation."
|
msgid "@%(reporter)s has flagged behavior by @%(reportee)s for moderation."
|
||||||
msgstr ""
|
msgstr "@%(reporter)s on merkinnyt käyttäjän @%(reportee)s toiminnan tarkastettavaksi."
|
||||||
|
|
||||||
#: bookwyrm/templates/email/moderation_report/html_content.html:21
|
#: bookwyrm/templates/email/moderation_report/html_content.html:21
|
||||||
#: bookwyrm/templates/email/moderation_report/text_content.html:15
|
#: bookwyrm/templates/email/moderation_report/text_content.html:15
|
||||||
|
@ -2272,8 +2280,8 @@ msgstr "Unohtuiko salasana?"
|
||||||
msgid "More about this site"
|
msgid "More about this site"
|
||||||
msgstr "Tietoja sivustosta"
|
msgstr "Tietoja sivustosta"
|
||||||
|
|
||||||
#: bookwyrm/templates/landing/password_reset.html:34
|
#: bookwyrm/templates/landing/password_reset.html:43
|
||||||
#: bookwyrm/templates/preferences/change_password.html:18
|
#: bookwyrm/templates/preferences/change_password.html:33
|
||||||
#: bookwyrm/templates/preferences/delete_user.html:20
|
#: bookwyrm/templates/preferences/delete_user.html:20
|
||||||
msgid "Confirm password:"
|
msgid "Confirm password:"
|
||||||
msgstr "Vahvista salasana:"
|
msgstr "Vahvista salasana:"
|
||||||
|
@ -2281,7 +2289,7 @@ msgstr "Vahvista salasana:"
|
||||||
#: bookwyrm/templates/landing/password_reset_request.html:14
|
#: bookwyrm/templates/landing/password_reset_request.html:14
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "A password reset link will be sent to <strong>%(email)s</strong> if there is an account using that email address."
|
msgid "A password reset link will be sent to <strong>%(email)s</strong> if there is an account using that email address."
|
||||||
msgstr ""
|
msgstr "Osoitteeseen <strong>%(email)s</strong> lähetetään linkki salasanan palauttamiseksi, mikäli osoite on yhdistetty johonkin käyttäjätiliin."
|
||||||
|
|
||||||
#: bookwyrm/templates/landing/password_reset_request.html:20
|
#: bookwyrm/templates/landing/password_reset_request.html:20
|
||||||
msgid "A link to reset your password will be sent to your email address"
|
msgid "A link to reset your password will be sent to your email address"
|
||||||
|
@ -2598,191 +2606,191 @@ msgstr "Tallennetut listat"
|
||||||
#: bookwyrm/templates/notifications/items/accept.html:18
|
#: bookwyrm/templates/notifications/items/accept.html:18
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> hyväksyi kutsusi liittyä ryhmään <a href=\"%(group_path)s\">%(group_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/accept.html:26
|
#: bookwyrm/templates/notifications/items/accept.html:26
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> hyväksyivät kutsusi liittyä ryhmään <a href=\"%(group_path)s\">%(group_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/accept.html:36
|
#: bookwyrm/templates/notifications/items/accept.html:36
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others accepted your invitation to join group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta hyväksyivät kutsusi liittyä ryhmään <a href=\"%(group_path)s\">%(group_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/add.html:33
|
#: bookwyrm/templates/notifications/items/add.html:33
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> lisäsi teoksen <em><a href=\"%(book_path)s\">%(book_title)s</a></em> listaasi <a href=\"%(list_path)s\">%(list_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/add.html:39
|
#: bookwyrm/templates/notifications/items/add.html:39
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em> to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ehdotti teosta <em><a href=\"%(book_path)s\">%(book_title)s</a></em> lisättäväksi listaasi <a href=\"%(list_curate_path)s\">%(list_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/add.html:47
|
#: bookwyrm/templates/notifications/items/add.html:47
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> and <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em> and <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> lisäsi teokset <em><a href=\"%(book_path)s\">%(book_title)s</a></em> ja <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> listaasi <a href=\"%(list_path)s\">%(list_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/add.html:54
|
#: bookwyrm/templates/notifications/items/add.html:54
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em> and <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em> and <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ehdotti teoksia <em><a href=\"%(book_path)s\">%(book_title)s</a></em> ja <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> lisättäväksi listaasi <a href=\"%(list_curate_path)s\">%(list_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/add.html:66
|
#: bookwyrm/templates/notifications/items/add.html:66
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other book to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other book to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
||||||
msgid_plural "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other books to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
msgid_plural "<a href=\"%(related_user_link)s\">%(related_user)s</a> added <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other books to your list \"<a href=\"%(list_path)s\">%(list_name)s</a>\""
|
||||||
msgstr[0] ""
|
msgstr[0] "<a href=\"%(related_user_link)s\">%(related_user)s</a> lisäsi teokset <em><a href=\"%(book_path)s\">%(book_title)s</a></em> ja <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> sekä %(display_count)s muun teoksen listaasi <a href=\"%(list_path)s\">%(list_name)s</a>"
|
||||||
msgstr[1] ""
|
msgstr[1] "<a href=\"%(related_user_link)s\">%(related_user)s</a> lisäsi teokset <em><a href=\"%(book_path)s\">%(book_title)s</a></em> ja <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> sekä %(display_count)s muuta teosta listaasi <a href=\"%(list_path)s\">%(list_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/add.html:82
|
#: bookwyrm/templates/notifications/items/add.html:82
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other book to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other book to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
||||||
msgid_plural "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other books to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
msgid_plural "<a href=\"%(related_user_link)s\">%(related_user)s</a> suggested adding <em><a href=\"%(book_path)s\">%(book_title)s</a></em>, <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em>, and %(display_count)s other books to your list \"<a href=\"%(list_curate_path)s\">%(list_name)s</a>\""
|
||||||
msgstr[0] ""
|
msgstr[0] "<a href=\"%(related_user_link)s\">%(related_user)s</a> ehdotti teoksia <em><a href=\"%(book_path)s\">%(book_title)s</a></em> ja <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> sekä %(display_count)s muuta teosta lisättäväksi listaasi <a href=\"%(list_curate_path)s\">%(list_name)s</a>"
|
||||||
msgstr[1] ""
|
msgstr[1] "<a href=\"%(related_user_link)s\">%(related_user)s</a> ehdotti teoksia <em><a href=\"%(book_path)s\">%(book_title)s</a></em> ja <em><a href=\"%(second_book_path)s\">%(second_book_title)s</a></em> sekä %(display_count)s muuta teosta lisättäväksi listaasi <a href=\"%(list_curate_path)s\">%(list_name)s</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:21
|
#: bookwyrm/templates/notifications/items/boost.html:21
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> kaiutti <a href=\"%(related_path)s\">arviotasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:27
|
#: bookwyrm/templates/notifications/items/boost.html:27
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> kaiuttivat <a href=\"%(related_path)s\">arviotasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:36
|
#: bookwyrm/templates/notifications/items/boost.html:36
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta kaiuttivat <a href=\"%(related_path)s\">arviotasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:44
|
#: bookwyrm/templates/notifications/items/boost.html:44
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> kaiutti <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevaa kommenttiasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:50
|
#: bookwyrm/templates/notifications/items/boost.html:50
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> kaiuttivat <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevaa kommenttiasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:59
|
#: bookwyrm/templates/notifications/items/boost.html:59
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta kaiuttivat <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevaa kommenttiasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:67
|
#: bookwyrm/templates/notifications/items/boost.html:67
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> kaiuttia <a href=\"%(related_path)s\">lainaustasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:73
|
#: bookwyrm/templates/notifications/items/boost.html:73
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> kaiuttivat <a href=\"%(related_path)s\">lainaustasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:82
|
#: bookwyrm/templates/notifications/items/boost.html:82
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta kaiuttivat <a href=\"%(related_path)s\">lainaustasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:90
|
#: bookwyrm/templates/notifications/items/boost.html:90
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> boosted your <a href=\"%(related_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> kaiutti <a href=\"%(related_path)s\">tilapäivitystäsi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:96
|
#: bookwyrm/templates/notifications/items/boost.html:96
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> boosted your <a href=\"%(related_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> kaiuttivat <a href=\"%(related_path)s\">tilapäivitystäsi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/boost.html:105
|
#: bookwyrm/templates/notifications/items/boost.html:105
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others boosted your <a href=\"%(related_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta kaiuttivat <a href=\"%(related_path)s\">tilapäivitystäsi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:21
|
#: bookwyrm/templates/notifications/items/fav.html:21
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> tykkäsi <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevasta arviostasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:27
|
#: bookwyrm/templates/notifications/items/fav.html:27
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> tykkäsivät <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevasta arviostasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:36
|
#: bookwyrm/templates/notifications/items/fav.html:36
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta tykkäsivät <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevasta arviostasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:44
|
#: bookwyrm/templates/notifications/items/fav.html:44
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> tykkäsi <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevasta kommentistasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:50
|
#: bookwyrm/templates/notifications/items/fav.html:50
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> tykkäsivät <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevasta kommentistasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:59
|
#: bookwyrm/templates/notifications/items/fav.html:59
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta tykkäsivät <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevasta kommentistasi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:67
|
#: bookwyrm/templates/notifications/items/fav.html:67
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> tykkäsi <a href=\"%(related_path)s\">lainauksestasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:73
|
#: bookwyrm/templates/notifications/items/fav.html:73
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> tykkäsivät <a href=\"%(related_path)s\">lainauksestasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:82
|
#: bookwyrm/templates/notifications/items/fav.html:82
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta tykkäsivät <a href=\"%(related_path)s\">lainauksestasi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:90
|
#: bookwyrm/templates/notifications/items/fav.html:90
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> liked your <a href=\"%(related_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> tykkäsi <a href=\"%(related_path)s\">tilapäivityksestäsi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:96
|
#: bookwyrm/templates/notifications/items/fav.html:96
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> liked your <a href=\"%(related_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> tykkäsivät <a href=\"%(related_path)s\">tilapäivityksestäsi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/fav.html:105
|
#: bookwyrm/templates/notifications/items/fav.html:105
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others liked your <a href=\"%(related_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta tykkäsivät <a href=\"%(related_path)s\">tilapäivityksestäsi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/follow.html:16
|
#: bookwyrm/templates/notifications/items/follow.html:16
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> followed you"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> followed you"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> alkoi seurata sinua"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/follow.html:20
|
#: bookwyrm/templates/notifications/items/follow.html:20
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> followed you"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> followed you"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> alkoivat seurata sinua"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/follow.html:25
|
#: bookwyrm/templates/notifications/items/follow.html:25
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others followed you"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others followed you"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta alkoivat seurata sinua"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/follow_request.html:15
|
#: bookwyrm/templates/notifications/items/follow_request.html:15
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> sent you a follow request"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> sent you a follow request"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> lähetti pyynnön saada seurata sinua"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/import.html:14
|
#: bookwyrm/templates/notifications/items/import.html:14
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -2792,7 +2800,7 @@ msgstr "<a href=\"%(url)s\">Tuonti</a> valmis."
|
||||||
#: bookwyrm/templates/notifications/items/invite.html:16
|
#: bookwyrm/templates/notifications/items/invite.html:16
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> invited you to join the group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> invited you to join the group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> kutsui sinut liittymään ryhmään ”<a href=\"%(group_path)s\">%(group_name)s</a>”"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/join.html:16
|
#: bookwyrm/templates/notifications/items/join.html:16
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -2802,37 +2810,37 @@ msgstr "liittyi ryhmääsi ”<a href=\"%(group_path)s\">%(group_name)s</a>”"
|
||||||
#: bookwyrm/templates/notifications/items/leave.html:18
|
#: bookwyrm/templates/notifications/items/leave.html:18
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> has left your group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> has left your group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> poistui ryhmästäsi ”<a href=\"%(group_path)s\">%(group_name)s</a>”"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/leave.html:26
|
#: bookwyrm/templates/notifications/items/leave.html:26
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> have left your group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and <a href=\"%(second_user_link)s\">%(second_user)s</a> have left your group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja <a href=\"%(second_user_link)s\">%(second_user)s</a> poistuivat ryhmästäsi ”<a href=\"%(group_path)s\">%(group_name)s</a>”"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/leave.html:36
|
#: bookwyrm/templates/notifications/items/leave.html:36
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others have left your group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> and %(other_user_display_count)s others have left your group \"<a href=\"%(group_path)s\">%(group_name)s</a>\""
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> ja %(other_user_display_count)s muuta poistuivat ryhmästäsi ”<a href=\"%(group_path)s\">%(group_name)s</a>”"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/mention.html:20
|
#: bookwyrm/templates/notifications/items/mention.html:20
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> mainitsi sinut <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevassa arviossaan</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/mention.html:26
|
#: bookwyrm/templates/notifications/items/mention.html:26
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> mainitsi sinut <a href=\"%(related_path)s\">teosta <em>%(book_title)s</em> koskevassa kommentissaan</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/mention.html:32
|
#: bookwyrm/templates/notifications/items/mention.html:32
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> mainitsi sinut <a href=\"%(related_path)s\">lainauksessaan teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/mention.html:38
|
#: bookwyrm/templates/notifications/items/mention.html:38
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> mentioned you in a <a href=\"%(related_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> mainitsi sinut <a href=\"%(related_path)s\">tilapäivityksessään</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/remove.html:17
|
#: bookwyrm/templates/notifications/items/remove.html:17
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -2847,29 +2855,34 @@ msgstr "Sinut on poistettu ryhmästä ”<a href=\"%(group_path)s\">%(group_name
|
||||||
#: bookwyrm/templates/notifications/items/reply.html:21
|
#: bookwyrm/templates/notifications/items/reply.html:21
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">review of <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">review of <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">vastasi</a> <a href=\"%(parent_path)s\">teosta <em>%(book_title)s</em> koskevaan arvioosi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/reply.html:27
|
#: bookwyrm/templates/notifications/items/reply.html:27
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">comment on <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">comment on <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">vastasi</a> <a href=\"%(parent_path)s\">teosta <em>%(book_title)s</em> koskevaan kommenttiisi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/reply.html:33
|
#: bookwyrm/templates/notifications/items/reply.html:33
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">quote from <em>%(book_title)s</em></a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">quote from <em>%(book_title)s</em></a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">vastasi</a> <a href=\"%(parent_path)s\">lainaukseesi teoksesta <em>%(book_title)s</em></a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/reply.html:39
|
#: bookwyrm/templates/notifications/items/reply.html:39
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">status</a>"
|
msgid "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">replied</a> to your <a href=\"%(parent_path)s\">status</a>"
|
||||||
msgstr ""
|
msgstr "<a href=\"%(related_user_link)s\">%(related_user)s</a> <a href=\"%(related_path)s\">vastasi</a> <a href=\"%(parent_path)s\">tilapäivitykseesi</a>"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/report.html:15
|
#: bookwyrm/templates/notifications/items/report.html:15
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "A new <a href=\"%(path)s\">report</a> needs moderation"
|
msgid "A new <a href=\"%(path)s\">report</a> needs moderation"
|
||||||
msgid_plural "%(display_count)s new <a href=\"%(path)s\">reports</a> need moderation"
|
msgid_plural "%(display_count)s new <a href=\"%(path)s\">reports</a> need moderation"
|
||||||
msgstr[0] ""
|
msgstr[0] "Uusi <a href=\"%(path)s\">raportti</a> odottaa tarkastusta"
|
||||||
msgstr[1] ""
|
msgstr[1] "%(display_count)s uutta <a href=\"%(path)s\">raporttia</a> odottaa tarkastusta"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/notifications/items/status_preview.html:4
|
||||||
|
#: bookwyrm/templates/snippets/status/content_status.html:73
|
||||||
|
msgid "Content warning"
|
||||||
|
msgstr "Sisältövaroitus"
|
||||||
|
|
||||||
#: bookwyrm/templates/notifications/items/update.html:16
|
#: bookwyrm/templates/notifications/items/update.html:16
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -3028,12 +3041,20 @@ msgstr "Ei estettyjä käyttäjiä."
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/change_password.html:4
|
#: bookwyrm/templates/preferences/change_password.html:4
|
||||||
#: bookwyrm/templates/preferences/change_password.html:7
|
#: bookwyrm/templates/preferences/change_password.html:7
|
||||||
#: bookwyrm/templates/preferences/change_password.html:21
|
#: bookwyrm/templates/preferences/change_password.html:37
|
||||||
#: bookwyrm/templates/preferences/layout.html:20
|
#: bookwyrm/templates/preferences/layout.html:20
|
||||||
msgid "Change Password"
|
msgid "Change Password"
|
||||||
msgstr "Vaihda salasana"
|
msgstr "Vaihda salasana"
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/change_password.html:14
|
#: bookwyrm/templates/preferences/change_password.html:15
|
||||||
|
msgid "Successfully changed password"
|
||||||
|
msgstr "Salasanan vaihto onnistui"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/preferences/change_password.html:22
|
||||||
|
msgid "Current password:"
|
||||||
|
msgstr "Nykyinen salasana:"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/preferences/change_password.html:28
|
||||||
msgid "New password:"
|
msgid "New password:"
|
||||||
msgstr "Uusi salasana:"
|
msgstr "Uusi salasana:"
|
||||||
|
|
||||||
|
@ -3125,6 +3146,10 @@ msgstr "CSV-vienti"
|
||||||
msgid "Your export will include all the books on your shelves, books you have reviewed, and books with reading activity."
|
msgid "Your export will include all the books on your shelves, books you have reviewed, and books with reading activity."
|
||||||
msgstr "Vienti sisältää kaikki hyllyissäsi olevat ja arvioimasi kirjat sekä kirjat, joita olet lukenut."
|
msgstr "Vienti sisältää kaikki hyllyissäsi olevat ja arvioimasi kirjat sekä kirjat, joita olet lukenut."
|
||||||
|
|
||||||
|
#: bookwyrm/templates/preferences/export.html:20
|
||||||
|
msgid "Download file"
|
||||||
|
msgstr "Lataa tiedosto"
|
||||||
|
|
||||||
#: bookwyrm/templates/preferences/layout.html:11
|
#: bookwyrm/templates/preferences/layout.html:11
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Käyttäjätili"
|
msgstr "Käyttäjätili"
|
||||||
|
@ -3193,7 +3218,7 @@ msgstr "Eteneminen"
|
||||||
#: bookwyrm/templates/readthrough/readthrough_modal.html:63
|
#: bookwyrm/templates/readthrough/readthrough_modal.html:63
|
||||||
#: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:32
|
#: bookwyrm/templates/snippets/reading_modals/finish_reading_modal.html:32
|
||||||
msgid "Finished reading"
|
msgid "Finished reading"
|
||||||
msgstr "Lopetti lukemisen"
|
msgstr "Luki loppuun"
|
||||||
|
|
||||||
#: bookwyrm/templates/readthrough/readthrough_list.html:9
|
#: bookwyrm/templates/readthrough/readthrough_list.html:9
|
||||||
msgid "Progress Updates:"
|
msgid "Progress Updates:"
|
||||||
|
@ -3353,13 +3378,13 @@ msgstr "Epätosi"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/announcements/announcement.html:57
|
#: bookwyrm/templates/settings/announcements/announcement.html:57
|
||||||
#: bookwyrm/templates/settings/announcements/edit_announcement.html:79
|
#: bookwyrm/templates/settings/announcements/edit_announcement.html:79
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:105
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:84
|
||||||
msgid "Start date:"
|
msgid "Start date:"
|
||||||
msgstr "Alkaen:"
|
msgstr "Alkaen:"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/announcements/announcement.html:62
|
#: bookwyrm/templates/settings/announcements/announcement.html:62
|
||||||
#: bookwyrm/templates/settings/announcements/edit_announcement.html:89
|
#: bookwyrm/templates/settings/announcements/edit_announcement.html:89
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:111
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:90
|
||||||
msgid "End date:"
|
msgid "End date:"
|
||||||
msgstr "Päättyen:"
|
msgstr "Päättyen:"
|
||||||
|
|
||||||
|
@ -3519,7 +3544,7 @@ msgid "Dashboard"
|
||||||
msgstr "Kojelauta"
|
msgstr "Kojelauta"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:15
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:15
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:134
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:113
|
||||||
msgid "Total users"
|
msgid "Total users"
|
||||||
msgstr "Käyttäjiä yhteensä"
|
msgstr "Käyttäjiä yhteensä"
|
||||||
|
|
||||||
|
@ -3537,66 +3562,31 @@ msgstr "Tilapäivityksiä"
|
||||||
msgid "Works"
|
msgid "Works"
|
||||||
msgstr "Teoksia"
|
msgstr "Teoksia"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:43
|
|
||||||
#, python-format
|
|
||||||
msgid "Your outgoing email address, <code>%(email_sender)s</code>, may be misconfigured."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:46
|
|
||||||
msgid "Check the <code>EMAIL_SENDER_NAME</code> and <code>EMAIL_SENDER_DOMAIN</code> in your <code>.env</code>."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:54
|
|
||||||
#, python-format
|
|
||||||
msgid "%(display_count)s open report"
|
|
||||||
msgid_plural "%(display_count)s open reports"
|
|
||||||
msgstr[0] "%(display_count)s käsittelemätön raportti"
|
|
||||||
msgstr[1] "%(display_count)s käsittelemätöntä raporttia"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:66
|
|
||||||
#, python-format
|
|
||||||
msgid "%(display_count)s domain needs review"
|
|
||||||
msgid_plural "%(display_count)s domains need review"
|
|
||||||
msgstr[0] "%(display_count)s verkkotunnus vaatii tarkistusta"
|
|
||||||
msgstr[1] "%(display_count)s verkkotunnusta vaatii tarkistusta"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:78
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:78
|
||||||
#, python-format
|
|
||||||
msgid "%(display_count)s invite request"
|
|
||||||
msgid_plural "%(display_count)s invite requests"
|
|
||||||
msgstr[0] "%(display_count)s kutsupyyntö"
|
|
||||||
msgstr[1] "%(display_count)s kutsupyyntöä"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:90
|
|
||||||
#, python-format
|
|
||||||
msgid "An update is available! You're running v%(current)s and the latest release is %(available)s."
|
|
||||||
msgstr "Päivitys saatavilla! Käytössäsi on versio %(current)s, ja viimeisin julkaistu versio on %(available)s."
|
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:99
|
|
||||||
msgid "Instance Activity"
|
msgid "Instance Activity"
|
||||||
msgstr "Palvelimen aktiivisuus"
|
msgstr "Palvelimen aktiivisuus"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:117
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:96
|
||||||
msgid "Interval:"
|
msgid "Interval:"
|
||||||
msgstr "Aikaväli:"
|
msgstr "Aikaväli:"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:121
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:100
|
||||||
msgid "Days"
|
msgid "Days"
|
||||||
msgstr "päivä"
|
msgstr "päivä"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:122
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:101
|
||||||
msgid "Weeks"
|
msgid "Weeks"
|
||||||
msgstr "viikko"
|
msgstr "viikko"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:140
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:119
|
||||||
msgid "User signup activity"
|
msgid "User signup activity"
|
||||||
msgstr "Rekisteröityneitä käyttäjiä"
|
msgstr "Rekisteröityneitä käyttäjiä"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:146
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:125
|
||||||
msgid "Status activity"
|
msgid "Status activity"
|
||||||
msgstr "Tilapäivityksiä"
|
msgstr "Tilapäivityksiä"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/dashboard/dashboard.html:152
|
#: bookwyrm/templates/settings/dashboard/dashboard.html:131
|
||||||
msgid "Works created"
|
msgid "Works created"
|
||||||
msgstr "Luotuja teoksia"
|
msgstr "Luotuja teoksia"
|
||||||
|
|
||||||
|
@ -3612,6 +3602,49 @@ msgstr "Tilapäivityksiä"
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Yhteensä"
|
msgstr "Yhteensä"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/domain_review.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid "%(display_count)s domain needs review"
|
||||||
|
msgid_plural "%(display_count)s domains need review"
|
||||||
|
msgstr[0] "%(display_count)s verkkotunnus vaatii tarkistusta"
|
||||||
|
msgstr[1] "%(display_count)s verkkotunnusta vaatii tarkistusta"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/email_config.html:8
|
||||||
|
#, python-format
|
||||||
|
msgid "Your outgoing email address, <code>%(email_sender)s</code>, may be misconfigured."
|
||||||
|
msgstr "Lähtevän sähköpostin osoitteesi <code>%(email_sender)s</code> saattaa olla määritelty väärin."
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/email_config.html:11
|
||||||
|
msgid "Check the <code>EMAIL_SENDER_NAME</code> and <code>EMAIL_SENDER_DOMAIN</code> in your <code>.env</code> file."
|
||||||
|
msgstr "Tarkista <code>.env</code>-tiedostosta asetukset <code>EMAIL_SENDER_NAME</code> ja <code>EMAIL_SENDER_DOMAIN</code>."
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/invites.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid "%(display_count)s invite request"
|
||||||
|
msgid_plural "%(display_count)s invite requests"
|
||||||
|
msgstr[0] "%(display_count)s kutsupyyntö"
|
||||||
|
msgstr[1] "%(display_count)s kutsupyyntöä"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/missing_conduct.html:8
|
||||||
|
msgid "Your instance is missing a code of conduct."
|
||||||
|
msgstr "Palvelimeltasi puuttuu käyttöehdot."
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/missing_privacy.html:8
|
||||||
|
msgid "Your instance is missing a privacy policy."
|
||||||
|
msgstr "Palvelimeltasi puuttuu tietosuojakäytäntö."
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/reports.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid "%(display_count)s open report"
|
||||||
|
msgid_plural "%(display_count)s open reports"
|
||||||
|
msgstr[0] "%(display_count)s käsittelemätön raportti"
|
||||||
|
msgstr[1] "%(display_count)s käsittelemätöntä raporttia"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/dashboard/warnings/update_version.html:8
|
||||||
|
#, python-format
|
||||||
|
msgid "An update is available! You're running v%(current)s and the latest release is %(available)s."
|
||||||
|
msgstr "Päivitys saatavilla! Käytössäsi on versio %(current)s, ja viimeisin julkaistu versio on %(available)s."
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/email_blocklist/domain_form.html:5
|
#: bookwyrm/templates/settings/email_blocklist/domain_form.html:5
|
||||||
#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10
|
#: bookwyrm/templates/settings/email_blocklist/email_blocklist.html:10
|
||||||
msgid "Add domain"
|
msgid "Add domain"
|
||||||
|
@ -3861,7 +3894,7 @@ msgstr "Lähetä kutsu"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:81
|
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:81
|
||||||
msgid "Re-send invite"
|
msgid "Re-send invite"
|
||||||
msgstr "Lähetä kutsu uudelleen"
|
msgstr "Uusi kutsu"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:101
|
#: bookwyrm/templates/settings/invites/manage_invite_requests.html:101
|
||||||
msgid "Ignore"
|
msgid "Ignore"
|
||||||
|
@ -4066,7 +4099,7 @@ msgstr "Raportti %(report_id)s: käyttäjän @%(username)s lisäämä linkki"
|
||||||
#: bookwyrm/templates/settings/reports/report_header.html:17
|
#: bookwyrm/templates/settings/reports/report_header.html:17
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Report #%(report_id)s: Link domain"
|
msgid "Report #%(report_id)s: Link domain"
|
||||||
msgstr ""
|
msgstr "Raportti %(report_id)s: Verkkotunnus"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/reports/report_header.html:24
|
#: bookwyrm/templates/settings/reports/report_header.html:24
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -4308,38 +4341,42 @@ msgstr "Salasana:"
|
||||||
msgid "Users: <small>%(instance_name)s</small>"
|
msgid "Users: <small>%(instance_name)s</small>"
|
||||||
msgstr "Käyttäjät: <small>%(instance_name)s</small>"
|
msgstr "Käyttäjät: <small>%(instance_name)s</small>"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:40
|
#: bookwyrm/templates/settings/users/user_admin.html:29
|
||||||
|
msgid "Deleted users"
|
||||||
|
msgstr "Poistetut käyttäjät"
|
||||||
|
|
||||||
|
#: bookwyrm/templates/settings/users/user_admin.html:44
|
||||||
#: bookwyrm/templates/settings/users/username_filter.html:5
|
#: bookwyrm/templates/settings/users/username_filter.html:5
|
||||||
msgid "Username"
|
msgid "Username"
|
||||||
msgstr "Käyttäjänimi"
|
msgstr "Käyttäjänimi"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:44
|
#: bookwyrm/templates/settings/users/user_admin.html:48
|
||||||
msgid "Date Added"
|
msgid "Date Added"
|
||||||
msgstr "Lisätty"
|
msgstr "Lisätty"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:48
|
#: bookwyrm/templates/settings/users/user_admin.html:52
|
||||||
msgid "Last Active"
|
msgid "Last Active"
|
||||||
msgstr "Viimeksi paikalla"
|
msgstr "Viimeksi paikalla"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:57
|
#: bookwyrm/templates/settings/users/user_admin.html:61
|
||||||
msgid "Remote instance"
|
msgid "Remote instance"
|
||||||
msgstr "Etäpalvelin"
|
msgstr "Etäpalvelin"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:74
|
#: bookwyrm/templates/settings/users/user_admin.html:81
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:28
|
#: bookwyrm/templates/settings/users/user_info.html:28
|
||||||
msgid "Active"
|
msgid "Active"
|
||||||
msgstr "Aktiivinen"
|
msgstr "Aktiivinen"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:79
|
#: bookwyrm/templates/settings/users/user_admin.html:86
|
||||||
msgid "Deleted"
|
msgid "Deleted"
|
||||||
msgstr ""
|
msgstr "Poistettu"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:85
|
#: bookwyrm/templates/settings/users/user_admin.html:92
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:32
|
#: bookwyrm/templates/settings/users/user_info.html:32
|
||||||
msgid "Inactive"
|
msgid "Inactive"
|
||||||
msgstr "Ei aktiivinen"
|
msgstr "Ei aktiivinen"
|
||||||
|
|
||||||
#: bookwyrm/templates/settings/users/user_admin.html:94
|
#: bookwyrm/templates/settings/users/user_admin.html:101
|
||||||
#: bookwyrm/templates/settings/users/user_info.html:127
|
#: bookwyrm/templates/settings/users/user_info.html:127
|
||||||
msgid "Not set"
|
msgid "Not set"
|
||||||
msgstr "Ei asetettu"
|
msgstr "Ei asetettu"
|
||||||
|
@ -4587,7 +4624,7 @@ msgstr "Aloitettu"
|
||||||
#: bookwyrm/templates/shelf/shelf.html:154
|
#: bookwyrm/templates/shelf/shelf.html:154
|
||||||
#: bookwyrm/templates/shelf/shelf.html:184
|
#: bookwyrm/templates/shelf/shelf.html:184
|
||||||
msgid "Finished"
|
msgid "Finished"
|
||||||
msgstr "Lopetettu"
|
msgstr "Luettu"
|
||||||
|
|
||||||
#: bookwyrm/templates/shelf/shelf.html:154
|
#: bookwyrm/templates/shelf/shelf.html:154
|
||||||
#: bookwyrm/templates/shelf/shelf.html:184
|
#: bookwyrm/templates/shelf/shelf.html:184
|
||||||
|
@ -5022,11 +5059,7 @@ msgstr "Keskeytä lukeminen"
|
||||||
|
|
||||||
#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:40
|
#: bookwyrm/templates/snippets/shelve_button/shelve_button_options.html:40
|
||||||
msgid "Finish reading"
|
msgid "Finish reading"
|
||||||
msgstr "Lopeta lukeminen"
|
msgstr "Luettu kokonaan"
|
||||||
|
|
||||||
#: bookwyrm/templates/snippets/status/content_status.html:73
|
|
||||||
msgid "Content warning"
|
|
||||||
msgstr "Sisältövaroitus"
|
|
||||||
|
|
||||||
#: bookwyrm/templates/snippets/status/content_status.html:80
|
#: bookwyrm/templates/snippets/status/content_status.html:80
|
||||||
msgid "Show status"
|
msgid "Show status"
|
||||||
|
@ -5088,12 +5121,12 @@ msgstr "arvosteli teoksen <a href=\"%(book_path)s\">%(book)s</a>:"
|
||||||
#: bookwyrm/templates/snippets/status/headers/read.html:10
|
#: bookwyrm/templates/snippets/status/headers/read.html:10
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "finished reading <a href=\"%(book_path)s\">%(book)s</a> by <a href=\"%(author_path)s\">%(author_name)s</a>"
|
msgid "finished reading <a href=\"%(book_path)s\">%(book)s</a> by <a href=\"%(author_path)s\">%(author_name)s</a>"
|
||||||
msgstr "lopetti teoksen <a href=\"%(author_path)s\">%(author_name)s</a>: <a href=\"%(book_path)s\">%(book)s</a> lukemisen"
|
msgstr "luki teoksen <a href=\"%(author_path)s\">%(author_name)s</a>: <a href=\"%(book_path)s\">%(book)s</a> loppuun"
|
||||||
|
|
||||||
#: bookwyrm/templates/snippets/status/headers/read.html:17
|
#: bookwyrm/templates/snippets/status/headers/read.html:17
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "finished reading <a href=\"%(book_path)s\">%(book)s</a>"
|
msgid "finished reading <a href=\"%(book_path)s\">%(book)s</a>"
|
||||||
msgstr "lopetti teoksen <a href=\"%(book_path)s\">%(book)s</a> lukemisen"
|
msgstr "luki teoksen <a href=\"%(book_path)s\">%(book)s</a> loppuun"
|
||||||
|
|
||||||
#: bookwyrm/templates/snippets/status/headers/reading.html:10
|
#: bookwyrm/templates/snippets/status/headers/reading.html:10
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -5323,7 +5356,7 @@ msgstr "Ei seuraajia, joita seuraat itse"
|
||||||
msgid "View profile and more"
|
msgid "View profile and more"
|
||||||
msgstr "Näytä profiili ja muita tietoja"
|
msgstr "Näytä profiili ja muita tietoja"
|
||||||
|
|
||||||
#: bookwyrm/templates/user_menu.html:72
|
#: bookwyrm/templates/user_menu.html:78
|
||||||
msgid "Log out"
|
msgid "Log out"
|
||||||
msgstr "Kirjaudu ulos"
|
msgstr "Kirjaudu ulos"
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue