mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-23 01:51:08 +00:00
Merge branch 'main' into user-migration
This commit is contained in:
commit
8477d0b89d
91 changed files with 6697 additions and 3892 deletions
1
.prettierrc
Normal file
1
.prettierrc
Normal file
|
@ -0,0 +1 @@
|
|||
'trailingComma': 'es5'
|
1
VERSION
Normal file
1
VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
0.7.0
|
|
@ -4,7 +4,11 @@ import sys
|
|||
|
||||
from .base_activity import ActivityEncoder, Signature, naive_parse
|
||||
from .base_activity import Link, Mention, Hashtag
|
||||
from .base_activity import ActivitySerializerError, resolve_remote_id
|
||||
from .base_activity import (
|
||||
ActivitySerializerError,
|
||||
resolve_remote_id,
|
||||
get_representative,
|
||||
)
|
||||
from .image import Document, Image
|
||||
from .note import Note, GeneratedNote, Article, Comment, Quotation
|
||||
from .note import Review, Rating
|
||||
|
|
|
@ -201,14 +201,13 @@ class Book(BookDataModel):
|
|||
@property
|
||||
def alt_text(self):
|
||||
"""image alt test"""
|
||||
text = self.title
|
||||
if self.edition_info:
|
||||
text += f" ({self.edition_info})"
|
||||
return text
|
||||
author = f"{name}: " if (name := self.author_text) else ""
|
||||
edition = f" ({info})" if (info := self.edition_info) else ""
|
||||
return f"{author}{self.title}{edition}"
|
||||
|
||||
def save(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""can't be abstract for query reasons, but you shouldn't USE it"""
|
||||
if not isinstance(self, Edition) and not isinstance(self, Work):
|
||||
if not isinstance(self, (Edition, Work)):
|
||||
raise ValueError("Books should be added as Editions or Works")
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
""" activitypub-aware django model fields """
|
||||
from dataclasses import MISSING
|
||||
from datetime import datetime
|
||||
import re
|
||||
from uuid import uuid4
|
||||
from urllib.parse import urljoin
|
||||
|
@ -534,8 +535,10 @@ class DateTimeField(ActivitypubFieldMixin, models.DateTimeField):
|
|||
return value.isoformat()
|
||||
|
||||
def field_from_activity(self, value, allow_external_connections=True):
|
||||
missing_fields = datetime(1970, 1, 1) # "2022-10" => "2022-10-01"
|
||||
try:
|
||||
date_value = dateutil.parser.parse(value)
|
||||
# TODO(dato): investigate `ignoretz=True` wrt bookwyrm#3028.
|
||||
date_value = dateutil.parser.parse(value, default=missing_fields)
|
||||
try:
|
||||
return timezone.make_aware(date_value)
|
||||
except ValueError:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" track progress of goodreads imports """
|
||||
from datetime import datetime
|
||||
import math
|
||||
import re
|
||||
import dateutil.parser
|
||||
|
@ -259,38 +260,30 @@ class ImportItem(models.Model):
|
|||
except ValueError:
|
||||
return None
|
||||
|
||||
def _parse_datefield(self, field, /):
|
||||
if not (date := self.normalized_data.get(field)):
|
||||
return None
|
||||
|
||||
defaults = datetime(1970, 1, 1) # "2022-10" => "2022-10-01"
|
||||
parsed = dateutil.parser.parse(date, default=defaults)
|
||||
|
||||
# Keep timezone if import already had one, else use default.
|
||||
return parsed if timezone.is_aware(parsed) else timezone.make_aware(parsed)
|
||||
|
||||
@property
|
||||
def date_added(self):
|
||||
"""when the book was added to this dataset"""
|
||||
if self.normalized_data.get("date_added"):
|
||||
parsed_date_added = dateutil.parser.parse(
|
||||
self.normalized_data.get("date_added")
|
||||
)
|
||||
|
||||
if timezone.is_aware(parsed_date_added):
|
||||
# Keep timezone if import already had one
|
||||
return parsed_date_added
|
||||
|
||||
return timezone.make_aware(parsed_date_added)
|
||||
return None
|
||||
return self._parse_datefield("date_added")
|
||||
|
||||
@property
|
||||
def date_started(self):
|
||||
"""when the book was started"""
|
||||
if self.normalized_data.get("date_started"):
|
||||
return timezone.make_aware(
|
||||
dateutil.parser.parse(self.normalized_data.get("date_started"))
|
||||
)
|
||||
return None
|
||||
return self._parse_datefield("date_started")
|
||||
|
||||
@property
|
||||
def date_read(self):
|
||||
"""the date a book was completed"""
|
||||
if self.normalized_data.get("date_finished"):
|
||||
return timezone.make_aware(
|
||||
dateutil.parser.parse(self.normalized_data.get("date_finished"))
|
||||
)
|
||||
return None
|
||||
return self._parse_datefield("date_finished")
|
||||
|
||||
@property
|
||||
def reads(self):
|
||||
|
|
|
@ -366,7 +366,8 @@ class Quotation(BookStatus):
|
|||
quote = re.sub(r"^<p>", '<p>"', self.quote)
|
||||
quote = re.sub(r"</p>$", '"</p>', quote)
|
||||
title, href = self.book.title, self.book.remote_id
|
||||
citation = f'— <a href="{href}"><i>{title}</i></a>'
|
||||
author = f"{name}: " if (name := self.book.author_text) else ""
|
||||
citation = f'— {author}<a href="{href}"><i>{title}</i></a>'
|
||||
if position := self._format_position():
|
||||
citation += f", {position}"
|
||||
return f"{quote} <p>{citation}</p>{self.content}"
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import AnyStr
|
|||
|
||||
from environs import Env
|
||||
|
||||
|
||||
import requests
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
@ -14,7 +15,13 @@ from django.core.exceptions import ImproperlyConfigured
|
|||
env = Env()
|
||||
env.read_env()
|
||||
DOMAIN = env("DOMAIN")
|
||||
VERSION = "0.6.5"
|
||||
|
||||
with open("VERSION", encoding="utf-8") as f:
|
||||
version = f.read()
|
||||
version = version.replace("\n", "")
|
||||
f.close()
|
||||
|
||||
VERSION = version
|
||||
|
||||
RELEASE_API = env(
|
||||
"RELEASE_API",
|
||||
|
@ -24,7 +31,7 @@ RELEASE_API = env(
|
|||
PAGE_LENGTH = env.int("PAGE_LENGTH", 15)
|
||||
DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English")
|
||||
|
||||
JS_CACHE = "b972a43c"
|
||||
JS_CACHE = "ac315a3b"
|
||||
|
||||
# email
|
||||
EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")
|
||||
|
@ -317,6 +324,7 @@ LANGUAGES = [
|
|||
|
||||
LANGUAGE_ARTICLES = {
|
||||
"English": {"the", "a", "an"},
|
||||
"Español (Spanish)": {"un", "una", "unos", "unas", "el", "la", "los", "las"},
|
||||
}
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
@charset "utf-8";
|
||||
|
||||
@import "vendor/bulma/bulma.sass";
|
||||
@import "bookwyrm/all.scss";
|
||||
@import "vendor/bulma/bulma";
|
||||
@import "bookwyrm/all";
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
@import "components/status";
|
||||
@import "components/tabs";
|
||||
@import "components/toggle";
|
||||
|
||||
@import "overrides/bulma_overrides";
|
||||
|
||||
@import "utilities/a11y";
|
||||
@import "utilities/alignments";
|
||||
@import "utilities/colors";
|
||||
|
@ -40,10 +38,12 @@ body {
|
|||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: $scrollbar-thumb;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: $scrollbar-track;
|
||||
}
|
||||
|
@ -89,7 +89,6 @@ button::-moz-focus-inner {
|
|||
/** Utilities not covered by Bulma
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
.tag.is-small {
|
||||
height: auto;
|
||||
}
|
||||
|
@ -144,7 +143,6 @@ button.button-paragraph {
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
/** States
|
||||
******************************************************************************/
|
||||
|
||||
|
@ -159,7 +157,6 @@ button.button-paragraph {
|
|||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
/* Notifications page
|
||||
******************************************************************************/
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
max-height: 100%;
|
||||
|
||||
/* Useful when stretching under-sized images. */
|
||||
image-rendering: optimizeQuality;
|
||||
image-rendering: optimizequality;
|
||||
image-rendering: smooth;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,20 +30,20 @@
|
|||
}
|
||||
|
||||
.copy-tooltip {
|
||||
overflow: visible;
|
||||
visibility: hidden;
|
||||
width: 140px;
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
margin-left: -30px;
|
||||
margin-top: -45px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
overflow: visible;
|
||||
visibility: hidden;
|
||||
width: 140px;
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
margin-left: -30px;
|
||||
margin-top: -45px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.copy-tooltip::after {
|
||||
|
@ -54,5 +54,5 @@
|
|||
margin-left: -60px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: #555 transparent transparent transparent;
|
||||
}
|
||||
border-color: #555 transparent transparent;
|
||||
}
|
||||
|
|
|
@ -44,12 +44,12 @@
|
|||
|
||||
.bw-tabs a:hover {
|
||||
border-bottom-color: transparent;
|
||||
color: $text
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.bw-tabs a.is-active {
|
||||
border-bottom-color: transparent;
|
||||
color: $link
|
||||
color: $link;
|
||||
}
|
||||
|
||||
.bw-tabs.is-left {
|
||||
|
|
Binary file not shown.
|
@ -43,6 +43,8 @@
|
|||
<glyph unicode="" glyph-name="barcode" d="M0 832h128v-640h-128zM192 832h64v-640h-64zM320 832h64v-640h-64zM512 832h64v-640h-64zM768 832h64v-640h-64zM960 832h64v-640h-64zM640 832h32v-640h-32zM448 832h32v-640h-32zM864 832h32v-640h-32zM0 128h64v-64h-64zM192 128h64v-64h-64zM320 128h64v-64h-64zM640 128h64v-64h-64zM960 128h64v-64h-64zM768 128h128v-64h-128zM448 128h128v-64h-128z" />
|
||||
<glyph unicode="" glyph-name="spinner" d="M384 832c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM655.53 719.53c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM832 448c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64s-64 28.654-64 64zM719.53 176.47c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64s-64 28.654-64 64zM448.002 64c0 0 0 0 0 0 0 35.346 28.654 64 64 64s64-28.654 64-64c0 0 0 0 0 0 0-35.346-28.654-64-64-64s-64 28.654-64 64zM176.472 176.47c0 0 0 0 0 0 0 35.346 28.654 64 64 64s64-28.654 64-64c0 0 0 0 0 0 0-35.346-28.654-64-64-64s-64 28.654-64 64zM144.472 719.53c0 0 0 0 0 0 0 53.019 42.981 96 96 96s96-42.981 96-96c0 0 0 0 0 0 0-53.019-42.981-96-96-96s-96 42.981-96 96zM56 448c0 39.765 32.235 72 72 72s72-32.235 72-72c0-39.765-32.235-72-72-72s-72 32.235-72 72z" />
|
||||
<glyph unicode="" glyph-name="search" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256z" />
|
||||
<glyph unicode="" glyph-name="eye" d="M512 768c-223.318 0-416.882-130.042-512-320 95.118-189.958 288.682-320 512-320 223.312 0 416.876 130.042 512 320-95.116 189.958-288.688 320-512 320zM764.45 598.296c60.162-38.374 111.142-89.774 149.434-150.296-38.292-60.522-89.274-111.922-149.436-150.296-75.594-48.218-162.89-73.704-252.448-73.704-89.56 0-176.858 25.486-252.452 73.704-60.158 38.372-111.138 89.772-149.432 150.296 38.292 60.524 89.274 111.924 149.434 150.296 3.918 2.5 7.876 4.922 11.86 7.3-9.96-27.328-15.41-56.822-15.41-87.596 0-141.382 114.616-256 256-256 141.382 0 256 114.618 256 256 0 30.774-5.452 60.268-15.408 87.598 3.978-2.378 7.938-4.802 11.858-7.302v0zM512 544c0-53.020-42.98-96-96-96s-96 42.98-96 96 42.98 96 96 96 96-42.982 96-96z" />
|
||||
<glyph unicode="" glyph-name="eye-blocked" d="M945.942 945.942c-18.746 18.744-49.136 18.744-67.882 0l-202.164-202.164c-51.938 15.754-106.948 24.222-163.896 24.222-223.318 0-416.882-130.042-512-320 41.122-82.124 100.648-153.040 173.022-207.096l-158.962-158.962c-18.746-18.746-18.746-49.136 0-67.882 9.372-9.374 21.656-14.060 33.94-14.060s24.568 4.686 33.942 14.058l864 864c18.744 18.746 18.744 49.138 0 67.884zM416 640c42.24 0 78.082-27.294 90.92-65.196l-121.724-121.724c-37.902 12.838-65.196 48.68-65.196 90.92 0 53.020 42.98 96 96 96zM110.116 448c38.292 60.524 89.274 111.924 149.434 150.296 3.918 2.5 7.876 4.922 11.862 7.3-9.962-27.328-15.412-56.822-15.412-87.596 0-54.89 17.286-105.738 46.7-147.418l-60.924-60.924c-52.446 36.842-97.202 83.882-131.66 138.342zM768 518c0 27.166-4.256 53.334-12.102 77.898l-321.808-321.808c24.568-7.842 50.742-12.090 77.91-12.090 141.382 0 256 114.618 256 256zM830.026 670.026l-69.362-69.362c1.264-0.786 2.53-1.568 3.786-2.368 60.162-38.374 111.142-89.774 149.434-150.296-38.292-60.522-89.274-111.922-149.436-150.296-75.594-48.218-162.89-73.704-252.448-73.704-38.664 0-76.902 4.76-113.962 14.040l-76.894-76.894c59.718-21.462 123.95-33.146 190.856-33.146 223.31 0 416.876 130.042 512 320-45.022 89.916-112.118 166.396-193.974 222.026z" />
|
||||
<glyph unicode="" glyph-name="star-empty" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-223.462-117.48 42.676 248.83-180.786 176.222 249.84 36.304 111.732 226.396 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
|
||||
<glyph unicode="" glyph-name="star-half" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-0.942-0.496 0.942 570.768 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
|
||||
<glyph unicode="" glyph-name="star-full" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538z" />
|
||||
|
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
@import "../vendor/bulma/sass/utilities/initial-variables.sass";
|
||||
@import "../vendor/bulma/sass/utilities/initial-variables";
|
||||
|
||||
/* Colors
|
||||
******************************************************************************/
|
||||
|
@ -16,7 +16,7 @@ $danger-light: #481922;
|
|||
$light: #393939;
|
||||
$red: #ffa1b4;
|
||||
$black: #000;
|
||||
$white-ter: hsl(0, 0%, 90%);
|
||||
$white-ter: hsl(0deg, 0%, 90%);
|
||||
|
||||
/* book cover standins */
|
||||
$no-cover-color: #002549;
|
||||
|
@ -79,7 +79,7 @@ $info-dark: #72b6ee;
|
|||
}
|
||||
|
||||
/* misc */
|
||||
$shadow: 0 0.5em 0.5em -0.125em rgba($black, 0.2), 0 0px 0 1px rgba($black, 0.02);
|
||||
$shadow: 0 0.5em 0.5em -0.125em rgba($black, 0.2), 0 0 0 1px rgba($black, 0.02);
|
||||
$card-header-shadow: 0 0.125em 0.25em rgba($black, 0.1);
|
||||
$invisible-overlay-background-color: rgba($black, 0.66);
|
||||
$progress-value-background-color: $border-light;
|
||||
|
@ -97,27 +97,23 @@ $family-secondary: $family-sans-serif;
|
|||
color: $grey-light !important;
|
||||
}
|
||||
|
||||
|
||||
.tabs li:not(.is-active) a {
|
||||
color: #2e7eb9 !important;
|
||||
}
|
||||
.tabs li:not(.is-active) a:hover {
|
||||
|
||||
.tabs li:not(.is-active) a:hover {
|
||||
border-bottom-color: #2e7eb9 !important;
|
||||
}
|
||||
|
||||
.tabs li:not(.is-active) a {
|
||||
color: #2e7eb9 !important;
|
||||
}
|
||||
|
||||
.tabs li.is-active a {
|
||||
color: #e6e6e6 !important;
|
||||
border-bottom-color: #e6e6e6 !important ;
|
||||
border-bottom-color: #e6e6e6 !important;
|
||||
}
|
||||
|
||||
|
||||
#qrcode svg {
|
||||
background-color: #a6a6a6;
|
||||
}
|
||||
|
||||
@import "../bookwyrm.scss";
|
||||
@import "../bookwyrm";
|
||||
@import "../vendor/icons.css";
|
||||
@import "../vendor/shepherd.scss";
|
||||
@import "../vendor/shepherd";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "../vendor/bulma/sass/utilities/derived-variables.sass";
|
||||
@import "../vendor/bulma/sass/utilities/derived-variables";
|
||||
|
||||
/* Colors
|
||||
******************************************************************************/
|
||||
|
@ -68,19 +68,16 @@ $family-secondary: $family-sans-serif;
|
|||
.tabs li:not(.is-active) a {
|
||||
color: #3273dc !important;
|
||||
}
|
||||
.tabs li:not(.is-active) a:hover {
|
||||
border-bottom-color: #3273dc !important;
|
||||
}
|
||||
|
||||
.tabs li:not(.is-active) a {
|
||||
color: #3273dc !important;
|
||||
.tabs li:not(.is-active) a:hover {
|
||||
border-bottom-color: #3273dc !important;
|
||||
}
|
||||
|
||||
.tabs li.is-active a {
|
||||
color: #4a4a4a !important;
|
||||
border-bottom-color: #4a4a4a !important ;
|
||||
border-bottom-color: #4a4a4a !important;
|
||||
}
|
||||
|
||||
|
||||
@import "../bookwyrm.scss";
|
||||
@import "../bookwyrm";
|
||||
@import "../vendor/icons.css";
|
||||
@import "../vendor/shepherd.scss";
|
||||
@import "../vendor/shepherd";
|
||||
|
|
6
bookwyrm/static/css/vendor/icons.css
vendored
6
bookwyrm/static/css/vendor/icons.css
vendored
|
@ -155,3 +155,9 @@
|
|||
.icon-barcode:before {
|
||||
content: "\e937";
|
||||
}
|
||||
.icon-eye:before {
|
||||
content: "\e9ce";
|
||||
}
|
||||
.icon-eye-blocked:before {
|
||||
content: "\e9d1";
|
||||
}
|
||||
|
|
|
@ -30,6 +30,12 @@ let BookWyrm = new (class {
|
|||
.querySelectorAll("[data-back]")
|
||||
.forEach((button) => button.addEventListener("click", this.back));
|
||||
|
||||
document
|
||||
.querySelectorAll("[data-password-icon]")
|
||||
.forEach((button) =>
|
||||
button.addEventListener("click", this.togglePasswordVisibility.bind(this))
|
||||
);
|
||||
|
||||
document
|
||||
.querySelectorAll('input[type="file"]')
|
||||
.forEach((node) => node.addEventListener("change", this.disableIfTooLarge.bind(this)));
|
||||
|
@ -820,4 +826,24 @@ let BookWyrm = new (class {
|
|||
|
||||
form.querySelector('input[name="preferred_timezone"]').value = tz;
|
||||
}
|
||||
|
||||
togglePasswordVisibility(event) {
|
||||
const iconElement = event.currentTarget.getElementsByTagName("button")[0];
|
||||
const passwordElementId = event.currentTarget.dataset.for;
|
||||
const passwordInputElement = document.getElementById(passwordElementId);
|
||||
|
||||
if (!passwordInputElement) return;
|
||||
|
||||
if (passwordInputElement.type === "password") {
|
||||
passwordInputElement.type = "text";
|
||||
this.addRemoveClass(iconElement, "icon-eye-blocked");
|
||||
this.addRemoveClass(iconElement, "icon-eye", true);
|
||||
} else {
|
||||
passwordInputElement.type = "password";
|
||||
this.addRemoveClass(iconElement, "icon-eye");
|
||||
this.addRemoveClass(iconElement, "icon-eye-blocked", true);
|
||||
}
|
||||
|
||||
this.toggleFocus(passwordElementId);
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -47,12 +47,11 @@
|
|||
.querySelectorAll("[data-remove]")
|
||||
.forEach((node) => node.addEventListener("click", removeInput));
|
||||
|
||||
// Get the element, add a keypress listener...
|
||||
// Get element, add a keypress listener...
|
||||
document.getElementById("subjects").addEventListener("keypress", function (e) {
|
||||
// e.target is the element where it listens!
|
||||
// if e.target is input field within the "subjects" div, do stuff
|
||||
// Linstening to element e.target
|
||||
// If e.target is an input field within "subjects" div preventDefault()
|
||||
if (e.target && e.target.nodeName == "INPUT") {
|
||||
// Item found, prevent default
|
||||
if (event.keyCode == 13) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
|
|
@ -144,14 +144,6 @@
|
|||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if author.isfdb %}
|
||||
<div>
|
||||
<a itemprop="sameAs" href="https://www.isfdb.org/cgi-bin/ea.cgi?{{ author.isfdb }}" target="_blank" rel="nofollow noopener noreferrer">
|
||||
{% trans "View ISFDB entry" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
|
|
@ -44,16 +44,18 @@
|
|||
{% endif %}
|
||||
|
||||
{% if book.series %}
|
||||
<meta itemprop="isPartOf" content="{{ book.series | escape }}">
|
||||
<meta itemprop="volumeNumber" content="{{ book.series_number }}">
|
||||
|
||||
<meta itemprop="position" content="{{ book.series_number }}">
|
||||
<span itemprop="isPartOf" itemscope itemtype="https://schema.org/BookSeries">
|
||||
{% if book.authors.exists %}
|
||||
<a href="{% url 'book-series-by' book.authors.first.id %}?series_name={{ book.series }}">
|
||||
<a href="{% url 'book-series-by' book.authors.first.id %}?series_name={{ book.series | urlencode }}"
|
||||
itemprop="url">
|
||||
{% endif %}
|
||||
{{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %}
|
||||
<span itemprop="name">{{ book.series }}</span>
|
||||
{% if book.series_number %} #{{ book.series_number }}{% endif %}
|
||||
{% if book.authors.exists %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
@ -186,8 +188,6 @@
|
|||
itemtype="https://schema.org/AggregateRating"
|
||||
>
|
||||
<meta itemprop="ratingValue" content="{{ rating|floatformat }}">
|
||||
{# @todo Is it possible to not hard-code the value? #}
|
||||
<meta itemprop="bestRating" content="5">
|
||||
<meta itemprop="reviewCount" content="{{ review_count }}">
|
||||
|
||||
<span>
|
||||
|
|
|
@ -40,16 +40,13 @@
|
|||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% with date=book.published_date|naturalday publisher=book.publishers|join:', ' %}
|
||||
{% if date or book.first_published_date or book.publishers %}
|
||||
{% if date or book.first_published_date %}
|
||||
{% if book.published_date or book.first_published_date %}
|
||||
<meta
|
||||
itemprop="datePublished"
|
||||
content="{{ book.first_published_date|default:book.published_date|date:'Y-m-d' }}"
|
||||
>
|
||||
{% endif %}
|
||||
<p>
|
||||
|
||||
{% comment %}
|
||||
@todo The publisher property needs to be an Organization or a Person. We’ll be using Thing which is the more generic ancestor.
|
||||
@see https://schema.org/Publisher
|
||||
|
@ -60,14 +57,14 @@
|
|||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if date and publisher %}
|
||||
{% with date=book.published_date|default:book.first_published_date|naturalday publisher=book.publishers|join:', ' %}
|
||||
{% if book.published_date and publisher %}
|
||||
{% blocktrans %}Published {{ date }} by {{ publisher }}.{% endblocktrans %}
|
||||
{% elif date %}
|
||||
{% blocktrans %}Published {{ date }}{% endblocktrans %}
|
||||
{% elif publisher %}
|
||||
{% blocktrans %}Published by {{ publisher }}.{% endblocktrans %}
|
||||
{% elif date %}
|
||||
{% blocktrans %}Published {{ date }}{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endspaceless %}
|
||||
|
|
|
@ -5,13 +5,18 @@
|
|||
{% include 'snippets/avatar.html' with user=user %}
|
||||
</div>
|
||||
|
||||
<div class="media-content">
|
||||
<div>
|
||||
<a href="{{ user.local_path }}">{{ user.display_name }}</a>
|
||||
<div class="media-content" itemprop="review" itemscope itemtype="https://schema.org/Review">
|
||||
<div itemprop="author"
|
||||
itemscope
|
||||
itemtype="https://schema.org/Person"
|
||||
>
|
||||
<a href="{{ user.local_path }}" itemprop="url">
|
||||
<span itemprop="name">{{ user.display_name }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="is-flex">
|
||||
<div class="is-flex" itemprop="reviewRating" itemscope itemtype="https://schema.org/Rating">
|
||||
<meta itemprop="ratingValue" content="{{ rating.rating|floatformat }}">
|
||||
<p class="mr-1">{% trans "rated it" %}</p>
|
||||
|
||||
{% include 'snippets/stars.html' with rating=rating.rating %}
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
{% block title %}{{ series_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="block">
|
||||
<h1 class="title">{{ series_name }}</h1>
|
||||
<div class="block" itemscope itemtype="https://schema.org/BookSeries">
|
||||
<h1 class="title" itemprop="name">{{ series_name }}</h1>
|
||||
<div class="subtitle" dir="auto">
|
||||
{% trans "Series by" %} <a
|
||||
href="{{ author.local_path }}"
|
||||
class="author {{ link_class }}"
|
||||
itemprop="author"
|
||||
itemprop="creator"
|
||||
itemscope
|
||||
itemtype="https://schema.org/Thing"
|
||||
itemtype="https://schema.org/Person"
|
||||
><span
|
||||
itemprop="name"
|
||||
>{{ author.name }}</span></a>
|
||||
|
@ -22,6 +22,7 @@
|
|||
<div class="columns is-multiline is-mobile">
|
||||
{% for book in books %}
|
||||
{% with book=book %}
|
||||
{# @todo Set `hasPart` property in some meaningful way #}
|
||||
<div class="column is-one-fifth-tablet is-half-mobile is-flex is-flex-direction-column">
|
||||
<div class="is-flex-grow-1 mb-3">
|
||||
<span class="subtitle">{% if book.series_number %}{% blocktrans with series_number=book.series_number %}Book {{ series_number }}{% endblocktrans %}{% else %}{% trans 'Unsorted Book' %}{% endif %}</span>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<base target="_blank">
|
||||
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% if site.favicon %}{% get_media_prefix %}{{ site.favicon }}{% else %}{% static "images/favicon.ico" %}{% endif %}">
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
{% if import_size_limit and import_limit_reset %}
|
||||
<div class="notification">
|
||||
<p>
|
||||
{% blocktrans count days=import_limit_reset with display_size=import_size_limit|intcomma %}
|
||||
{% blocktrans trimmed count days=import_limit_reset with display_size=import_size_limit|intcomma %}
|
||||
Currently, you are allowed to import {{ display_size }} books every {{ import_limit_reset }} day.
|
||||
{% plural %}
|
||||
Currently, you are allowed to import {{ import_size_limit }} books every {{ import_limit_reset }} days.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% if site.favicon %}{% get_media_prefix %}{{ site.favicon }}{% else %}{% static "images/favicon.ico" %}{% endif %}">
|
||||
<link rel="apple-touch-icon" href="{% if site.logo %}{{ media_full_url }}{{ site.logo }}{% else %}{% static "images/logo.png" %}{% endif %}">
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
{% block opengraph %}
|
||||
{% include 'snippets/opengraph.html' %}
|
||||
|
@ -129,7 +130,12 @@
|
|||
</div>
|
||||
<div class="column">
|
||||
<label class="is-sr-only" for="id_password">{% trans "Password:" %}</label>
|
||||
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password" placeholder="{% trans 'password' %}">
|
||||
<div class="control has-icons-right">
|
||||
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password" placeholder="{% trans 'password' %}">
|
||||
<span data-password-icon data-for="id_password" class="icon is-right is-clickable">
|
||||
<button type="button" aria-controls="id_password" class="icon-eye-blocked" title="{% trans 'Show/Hide password' %}"></button>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help"><a href="{% url 'password-reset' %}">{% trans "Forgot your password?" %}</a></p>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
|
|
14
bookwyrm/templates/manifest.json
Normal file
14
bookwyrm/templates/manifest.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% load static %}
|
||||
{
|
||||
"name": "{{ site.name }}",
|
||||
"description": "{{ site.description }}",
|
||||
"icons": [
|
||||
{
|
||||
"src": "{% if site.logo %}{{ media_full_url }}{{ site.logo }}{% else %}{% static 'images/logo.png' %}{% endif %}",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone"
|
||||
}
|
|
@ -75,13 +75,13 @@
|
|||
{% include 'snippets/form_errors.html' with errors_list=form.invite_request_text.errors id="desc_invite_request_text" %}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_invite_requests_question">
|
||||
<label class="label">
|
||||
{{ form.invite_request_question }}
|
||||
{% trans "Set a question for invite requests" %}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_invite_question_text">
|
||||
<label class="label">
|
||||
{% trans "Question:" %}
|
||||
{{ form.invite_question_text }}
|
||||
</label>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
{% include 'snippets/form_errors.html' with errors_list=form.invite_request_text.errors id="desc_invite_request_text" %}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_invite_requests_question">
|
||||
<label class="label">
|
||||
{{ form.invite_request_question }}
|
||||
{% trans "Set a question for invite requests" %}
|
||||
</label>
|
||||
|
|
|
@ -6,14 +6,6 @@
|
|||
{% load humanize %}
|
||||
|
||||
{% with status_type=status.status_type %}
|
||||
<div
|
||||
class="block"
|
||||
{% if status_type == "Review" %}
|
||||
itemprop="rating"
|
||||
itemtype="https://schema.org/Rating"
|
||||
{% endif %}
|
||||
>
|
||||
|
||||
<div class="columns is-gapless">
|
||||
{% if not hide_book %}
|
||||
{% with book=status.book|default:status.mention_books.first %}
|
||||
|
@ -58,9 +50,6 @@
|
|||
{% endif %}
|
||||
>
|
||||
<meta itemprop="ratingValue" content="{{ status.rating|floatformat }}">
|
||||
|
||||
{# @todo Is it possible to not hard-code the value? #}
|
||||
<meta itemprop="bestRating" content="5">
|
||||
</span>
|
||||
{% include 'snippets/stars.html' with rating=status.rating %}
|
||||
</h4>
|
||||
|
@ -154,6 +143,5 @@
|
|||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endwith %}
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
>
|
||||
<span class="is-hidden" {{ rating_type }}>
|
||||
<meta itemprop="ratingValue" content="{{ status.rating|floatformat }}">
|
||||
|
||||
{# @todo Is it possible to not hard-code the value? #}
|
||||
<meta itemprop="bestRating" content="5">
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
@ -92,7 +92,23 @@ class Book(TestCase):
|
|||
book.published_date = timezone.make_aware(parse("2020"))
|
||||
book.save()
|
||||
self.assertEqual(book.edition_info, "worm, Glorbish language, 2020")
|
||||
self.assertEqual(book.alt_text, "Test Edition (worm, Glorbish language, 2020)")
|
||||
|
||||
def test_alt_text(self):
|
||||
"""text slug used for cover images"""
|
||||
book = models.Edition.objects.create(title="Test Edition")
|
||||
author = models.Author.objects.create(name="Author Name")
|
||||
|
||||
self.assertEqual(book.alt_text, "Test Edition")
|
||||
|
||||
book.authors.set([author])
|
||||
book.save()
|
||||
|
||||
self.assertEqual(book.alt_text, "Author Name: Test Edition")
|
||||
|
||||
book.physical_format = "worm"
|
||||
book.published_date = timezone.make_aware(parse("2022"))
|
||||
|
||||
self.assertEqual(book.alt_text, "Author Name: Test Edition (worm, 2022)")
|
||||
|
||||
def test_get_rank(self):
|
||||
"""sets the data quality index for the book"""
|
||||
|
|
|
@ -314,6 +314,29 @@ class Status(TestCase):
|
|||
)
|
||||
self.assertEqual(activity["attachment"][0]["name"], "Test Edition")
|
||||
|
||||
def test_quotation_with_author_to_pure_activity(self, *_):
|
||||
"""serialization of quotation of a book with author and edition info"""
|
||||
self.book.authors.set([models.Author.objects.create(name="Author Name")])
|
||||
self.book.physical_format = "worm"
|
||||
self.book.save()
|
||||
status = models.Quotation.objects.create(
|
||||
quote="quote",
|
||||
content="",
|
||||
user=self.local_user,
|
||||
book=self.book,
|
||||
)
|
||||
activity = status.to_activity(pure=True)
|
||||
self.assertEqual(
|
||||
activity["content"],
|
||||
(
|
||||
f'quote <p>— Author Name: <a href="{self.book.remote_id}">'
|
||||
"<i>Test Edition</i></a></p>"
|
||||
),
|
||||
)
|
||||
self.assertEqual(
|
||||
activity["attachment"][0]["name"], "Author Name: Test Edition (worm)"
|
||||
)
|
||||
|
||||
def test_quotation_page_serialization(self, *_):
|
||||
"""serialization of quotation page position"""
|
||||
tests = [
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
|
|||
from django.template.response import TemplateResponse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import forms, models, views
|
||||
from bookwyrm.tests.validate_html import validate_html
|
||||
|
@ -128,7 +129,7 @@ class ImportViews(TestCase):
|
|||
|
||||
def test_get_average_import_time_with_data(self):
|
||||
"""Now, with data"""
|
||||
now = datetime.datetime.now()
|
||||
now = timezone.now()
|
||||
two_hours_ago = now - datetime.timedelta(hours=2)
|
||||
four_hours_ago = now - datetime.timedelta(hours=4)
|
||||
models.ImportJob.objects.create(
|
||||
|
@ -152,7 +153,7 @@ class ImportViews(TestCase):
|
|||
|
||||
def test_get_average_import_time_ignore_stopped(self):
|
||||
"""Don't include stopped, do include no status"""
|
||||
now = datetime.datetime.now()
|
||||
now = timezone.now()
|
||||
two_hours_ago = now - datetime.timedelta(hours=2)
|
||||
four_hours_ago = now - datetime.timedelta(hours=4)
|
||||
models.ImportJob.objects.create(
|
||||
|
|
|
@ -156,7 +156,7 @@ class Views(TestCase):
|
|||
response = view(request)
|
||||
|
||||
validate_html(response.render())
|
||||
self.assertFalse("results" in response.context_data)
|
||||
self.assertTrue("results" in response.context_data)
|
||||
|
||||
def test_search_lists(self):
|
||||
"""searches remote connectors"""
|
||||
|
|
|
@ -72,7 +72,7 @@ class SetupViews(TestCase):
|
|||
self.site.refresh_from_db()
|
||||
self.assertFalse(self.site.install_mode)
|
||||
|
||||
user = models.User.objects.get()
|
||||
user = models.User.objects.first()
|
||||
self.assertTrue(user.is_active)
|
||||
self.assertTrue(user.is_superuser)
|
||||
self.assertTrue(user.is_staff)
|
||||
|
|
|
@ -34,6 +34,12 @@ urlpatterns = [
|
|||
"robots.txt",
|
||||
TemplateView.as_view(template_name="robots.txt", content_type="text/plain"),
|
||||
),
|
||||
path(
|
||||
"manifest.json",
|
||||
TemplateView.as_view(
|
||||
template_name="manifest.json", content_type="application/json"
|
||||
),
|
||||
),
|
||||
# federation endpoints
|
||||
re_path(r"^inbox/?$", views.Inbox.as_view(), name="inbox"),
|
||||
re_path(rf"{LOCAL_USER_PATH}/inbox/?$", views.Inbox.as_view(), name="user_inbox"),
|
||||
|
|
|
@ -91,18 +91,15 @@ def book_search(request):
|
|||
|
||||
|
||||
def user_search(request):
|
||||
"""cool kids members only user search"""
|
||||
"""user search: search for a user"""
|
||||
viewer = request.user
|
||||
query = request.GET.get("q")
|
||||
query = query.strip()
|
||||
data = {"type": "user", "query": query}
|
||||
# logged out viewers can't search users
|
||||
if not viewer.is_authenticated:
|
||||
return TemplateResponse(request, "search/user.html", data)
|
||||
|
||||
# use webfinger for mastodon style account@domain.com username to load the user if
|
||||
# they don't exist locally (handle_remote_webfinger will check the db)
|
||||
if re.match(regex.FULL_USERNAME, query):
|
||||
if re.match(regex.FULL_USERNAME, query) and viewer.is_authenticated:
|
||||
handle_remote_webfinger(query)
|
||||
|
||||
results = (
|
||||
|
@ -118,6 +115,11 @@ def user_search(request):
|
|||
)
|
||||
.order_by("-similarity")
|
||||
)
|
||||
|
||||
# don't expose remote users
|
||||
if not viewer.is_authenticated:
|
||||
results = results.filter(local=True)
|
||||
|
||||
paginated = Paginator(results, PAGE_LENGTH)
|
||||
page = paginated.get_page(request.GET.get("page"))
|
||||
data["results"] = page
|
||||
|
|
|
@ -9,6 +9,7 @@ from django.shortcuts import redirect
|
|||
from django.template.response import TemplateResponse
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm.activitypub import get_representative
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm import settings
|
||||
from bookwyrm.utils import regex
|
||||
|
@ -96,4 +97,5 @@ class CreateAdmin(View):
|
|||
login(request, user)
|
||||
site.install_mode = False
|
||||
site.save()
|
||||
get_representative() # create the instance user
|
||||
return redirect("settings-site")
|
||||
|
|
80
bump-version.sh
Executable file
80
bump-version.sh
Executable file
|
@ -0,0 +1,80 @@
|
|||
#!/bin/bash
|
||||
|
||||
NOW="$(date +'%B %d, %Y')"
|
||||
RED="\033[1;31m"
|
||||
GREEN="\033[0;32m"
|
||||
YELLOW="\033[1;33m"
|
||||
BLUE="\033[1;34m"
|
||||
PURPLE="\033[1;35m"
|
||||
CYAN="\033[1;36m"
|
||||
WHITE="\033[1;37m"
|
||||
RESET="\033[0m"
|
||||
|
||||
LATEST_HASH=`git log --pretty=format:'%h' -n 1`
|
||||
|
||||
QUESTION_FLAG="${GREEN}?"
|
||||
WARNING_FLAG="${YELLOW}!"
|
||||
NOTICE_FLAG="${CYAN}❯"
|
||||
|
||||
# ADJUSTMENTS_MSG="${QUESTION_FLAG} ${CYAN}Now you can make adjustments to ${WHITE}CHANGELOG.md${CYAN}. Then press enter to continue."
|
||||
PUSHING_MSG="${NOTICE_FLAG} Pushing new version to the ${WHITE}origin${CYAN}..."
|
||||
|
||||
if [ -f VERSION ]; then
|
||||
BASE_STRING=`cat VERSION`
|
||||
BASE_LIST=(`echo $BASE_STRING | tr '.' ' '`)
|
||||
V_MAJOR=${BASE_LIST[0]}
|
||||
V_MINOR=${BASE_LIST[1]}
|
||||
V_PATCH=${BASE_LIST[2]}
|
||||
echo -e "${NOTICE_FLAG} Current version: ${WHITE}$BASE_STRING"
|
||||
echo -e "${NOTICE_FLAG} Latest commit hash: ${WHITE}$LATEST_HASH"
|
||||
V_MINOR=$((V_MINOR + 1))
|
||||
V_PATCH=0
|
||||
SUGGESTED_VERSION="$V_MAJOR.$V_MINOR.$V_PATCH"
|
||||
echo -ne "${QUESTION_FLAG} ${CYAN}Enter a version number [${WHITE}$SUGGESTED_VERSION${CYAN}]: "
|
||||
read INPUT_STRING
|
||||
if [ "$INPUT_STRING" = "" ]; then
|
||||
INPUT_STRING=$SUGGESTED_VERSION
|
||||
fi
|
||||
echo -e "${NOTICE_FLAG} Will set new version to be ${WHITE}$INPUT_STRING"
|
||||
echo $INPUT_STRING > VERSION
|
||||
# echo "## $INPUT_STRING ($NOW)" > tmpfile
|
||||
# git log --pretty=format:" - %s" "v$BASE_STRING"...HEAD >> tmpfile
|
||||
# echo "" >> tmpfile
|
||||
# echo "" >> tmpfile
|
||||
# cat CHANGELOG.md >> tmpfile
|
||||
# mv tmpfile CHANGELOG.md
|
||||
# echo -e "$ADJUSTMENTS_MSG"
|
||||
# read
|
||||
echo -e "$PUSHING_MSG"
|
||||
# git add CHANGELOG.md
|
||||
git add VERSION
|
||||
git commit -m "Bump version to ${INPUT_STRING}."
|
||||
git tag -a -m "Tag version ${INPUT_STRING}." "v$INPUT_STRING"
|
||||
git push origin --tags
|
||||
else
|
||||
echo -e "${WARNING_FLAG} Could not find a VERSION file."
|
||||
echo -ne "${QUESTION_FLAG} ${CYAN}Do you want to create a version file and start from scratch? [${WHITE}y${CYAN}]: "
|
||||
read RESPONSE
|
||||
if [ "$RESPONSE" = "" ]; then RESPONSE="y"; fi
|
||||
if [ "$RESPONSE" = "Y" ]; then RESPONSE="y"; fi
|
||||
if [ "$RESPONSE" = "Yes" ]; then RESPONSE="y"; fi
|
||||
if [ "$RESPONSE" = "yes" ]; then RESPONSE="y"; fi
|
||||
if [ "$RESPONSE" = "YES" ]; then RESPONSE="y"; fi
|
||||
if [ "$RESPONSE" = "y" ]; then
|
||||
echo "0.1.0" > VERSION
|
||||
# echo "## 0.1.0 ($NOW)" > CHANGELOG.md
|
||||
# git log --pretty=format:" - %s" >> CHANGELOG.md
|
||||
# echo "" >> CHANGELOG.md
|
||||
# echo "" >> CHANGELOG.md
|
||||
# echo -e "$ADJUSTMENTS_MSG"
|
||||
# read
|
||||
echo -e "$PUSHING_MSG"
|
||||
# git add CHANGELOG.md
|
||||
git add VERSION
|
||||
git commit -m "Add VERSION and CHANGELOG.md files, Bump version to v0.1.0."
|
||||
git tag -a -m "Tag version 0.1.0." "v0.1.0"
|
||||
git push origin --tags
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "${NOTICE_FLAG} Finished."
|
18
bw-dev
18
bw-dev
|
@ -188,27 +188,25 @@ case "$CMD" in
|
|||
;;
|
||||
prettier)
|
||||
prod_error
|
||||
$DOCKER_COMPOSE run --rm dev-tools npx prettier --write bookwyrm/static/js/*.js
|
||||
$DOCKER_COMPOSE run --rm dev-tools prettier --write bookwyrm/static/js/*.js
|
||||
;;
|
||||
eslint)
|
||||
prod_error
|
||||
$DOCKER_COMPOSE run --rm dev-tools npx eslint bookwyrm/static --ext .js
|
||||
$DOCKER_COMPOSE run --rm dev-tools eslint bookwyrm/static --ext .js
|
||||
;;
|
||||
stylelint)
|
||||
prod_error
|
||||
$DOCKER_COMPOSE run --rm dev-tools npx stylelint \
|
||||
bookwyrm/static/css/bookwyrm.scss bookwyrm/static/css/bookwyrm/**/*.scss --fix \
|
||||
--config dev-tools/.stylelintrc.js
|
||||
$DOCKER_COMPOSE run --rm dev-tools stylelint --fix bookwyrm/static/css \
|
||||
--config dev-tools/.stylelintrc.js --ignore-path dev-tools/.stylelintignore
|
||||
;;
|
||||
formatters)
|
||||
prod_error
|
||||
runweb pylint 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 eslint bookwyrm/static --ext .js
|
||||
$DOCKER_COMPOSE run --rm dev-tools npx stylelint \
|
||||
bookwyrm/static/css/bookwyrm.scss bookwyrm/static/css/bookwyrm/**/*.scss --fix \
|
||||
--config dev-tools/.stylelintrc.js
|
||||
$DOCKER_COMPOSE run --rm dev-tools prettier --write bookwyrm/static/js/*.js
|
||||
$DOCKER_COMPOSE run --rm dev-tools eslint bookwyrm/static --ext .js
|
||||
$DOCKER_COMPOSE run --rm dev-tools stylelint --fix bookwyrm/static/css \
|
||||
--config dev-tools/.stylelintrc.js --ignore-path dev-tools/.stylelintignore
|
||||
;;
|
||||
mypy)
|
||||
prod_error
|
||||
|
|
|
@ -5,10 +5,30 @@ After=network.target postgresql.service redis.service
|
|||
[Service]
|
||||
User=bookwyrm
|
||||
Group=bookwyrm
|
||||
WorkingDirectory=/opt/bookwyrm/
|
||||
WorkingDirectory=/opt/bookwyrm
|
||||
ExecStart=/opt/bookwyrm/venv/bin/celery -A celerywyrm beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
|
||||
StandardOutput=journal
|
||||
StandardError=inherit
|
||||
ProtectSystem=strict
|
||||
ProtectHome=tmpfs
|
||||
InaccessiblePaths=-/media -/mnt -/srv
|
||||
PrivateTmp=yes
|
||||
TemporaryFileSystem=/var /run /opt
|
||||
PrivateUsers=true
|
||||
PrivateDevices=true
|
||||
BindReadOnlyPaths=/opt/bookwyrm
|
||||
BindPaths=/opt/bookwyrm/images /opt/bookwyrm/static /var/run/postgresql
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=true
|
||||
PrivateMounts=true
|
||||
ProtectHostname=true
|
||||
ProtectClock=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectControlGroups=true
|
||||
RestrictRealtime=true
|
||||
RestrictNamespaces=net
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -5,10 +5,30 @@ After=network.target postgresql.service redis.service
|
|||
[Service]
|
||||
User=bookwyrm
|
||||
Group=bookwyrm
|
||||
WorkingDirectory=/opt/bookwyrm/
|
||||
WorkingDirectory=/opt/bookwyrm
|
||||
ExecStart=/opt/bookwyrm/venv/bin/celery -A celerywyrm worker -l info -Q high_priority,medium_priority,low_priority,streams,images,suggested_users,email,connectors,lists,inbox,imports,import_triggered,broadcast,misc
|
||||
StandardOutput=journal
|
||||
StandardError=inherit
|
||||
ProtectSystem=strict
|
||||
ProtectHome=tmpfs
|
||||
InaccessiblePaths=-/media -/mnt -/srv
|
||||
PrivateTmp=yes
|
||||
TemporaryFileSystem=/var /run /opt
|
||||
PrivateUsers=true
|
||||
PrivateDevices=true
|
||||
BindReadOnlyPaths=/opt/bookwyrm
|
||||
BindPaths=/opt/bookwyrm/images /opt/bookwyrm/static /var/run/postgresql
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=true
|
||||
PrivateMounts=true
|
||||
ProtectHostname=true
|
||||
ProtectClock=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectControlGroups=true
|
||||
RestrictRealtime=true
|
||||
RestrictNamespaces=net
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -5,10 +5,30 @@ After=network.target postgresql.service redis.service
|
|||
[Service]
|
||||
User=bookwyrm
|
||||
Group=bookwyrm
|
||||
WorkingDirectory=/opt/bookwyrm/
|
||||
WorkingDirectory=/opt/bookwyrm
|
||||
ExecStart=/opt/bookwyrm/venv/bin/gunicorn bookwyrm.wsgi:application --bind 0.0.0.0:8000
|
||||
StandardOutput=journal
|
||||
StandardError=inherit
|
||||
ProtectSystem=strict
|
||||
ProtectHome=tmpfs
|
||||
InaccessiblePaths=-/media -/mnt -/srv
|
||||
PrivateTmp=yes
|
||||
TemporaryFileSystem=/var /run /opt
|
||||
PrivateUsers=true
|
||||
PrivateDevices=true
|
||||
BindReadOnlyPaths=/opt/bookwyrm
|
||||
BindPaths=/opt/bookwyrm/images /opt/bookwyrm/static /var/run/postgresql
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=true
|
||||
PrivateMounts=true
|
||||
ProtectHostname=true
|
||||
ProtectClock=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectControlGroups=true
|
||||
RestrictRealtime=true
|
||||
RestrictNamespaces=net
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
**/vendor/**
|
||||
**/fonts/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* global module */
|
||||
|
||||
module.exports = {
|
||||
"extends": "stylelint-config-standard",
|
||||
"extends": "stylelint-config-standard-scss",
|
||||
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
|
@ -18,5 +18,13 @@ module.exports = {
|
|||
"declaration-block-no-redundant-longhand-properties": null,
|
||||
"no-descending-specificity": null,
|
||||
"alpha-value-notation": null
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [ "../**/themes/bookwyrm-*.scss" ],
|
||||
"rules": {
|
||||
"no-invalid-position-at-import-rule": null
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
FROM python:3.9
|
||||
WORKDIR /app/dev-tools
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV PATH="/app/dev-tools/node_modules/.bin:$PATH"
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV NPM_CONFIG_UPDATE_NOTIFIER=false
|
||||
ENV PIP_ROOT_USER_ACTION=ignore PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
COPY nodejs.sources /etc/apt/sources.list.d/
|
||||
COPY package.json requirements.txt .stylelintrc.js .stylelintignore /app/dev-tools/
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y nodejs && \
|
||||
pip install -r requirements.txt && \
|
||||
npm install .
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json requirements.txt .stylelintrc.js .stylelintignore /app/
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
RUN apt-get update && apt-get install -y curl
|
||||
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
|
||||
RUN apt-get install -y nodejs && apt-get clean
|
||||
RUN npm install .
|
||||
|
|
34
dev-tools/nodejs.sources
Normal file
34
dev-tools/nodejs.sources
Normal file
|
@ -0,0 +1,34 @@
|
|||
Types: deb
|
||||
URIs: https://deb.nodesource.com/node_18.x
|
||||
Suites: nodistro
|
||||
Components: main
|
||||
Signed-By:
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
.
|
||||
mQENBFdDN1ABCADaNd/I3j3tn40deQNgz7hB2NvT+syXe6k4ZmdiEcOfBvFrkS8B
|
||||
hNS67t93etHsxEy7E0qwsZH32bKazMqe9zDwoa3aVImryjh6SHC9lMtW27JPHFeM
|
||||
Srkt9YmH1WMwWcRO6eSY9B3PpazquhnvbammLuUojXRIxkDroy6Fw4UKmUNSRr32
|
||||
9Ej87jRoR1B2/57Kfp2Y4+vFGGzSvh3AFQpBHq51qsNHALU6+8PjLfIt+5TPvaWR
|
||||
TB+kAZnQZkaIQM2nr1n3oj6ak2RATY/+kjLizgFWzgEfbCrbsyq68UoY5FPBnu4Z
|
||||
E3iDZpaIqwKr0seUC7iA1xM5eHi5kty1oB7HABEBAAG0Ik5Tb2xpZCA8bnNvbGlk
|
||||
LWdwZ0Bub2Rlc291cmNlLmNvbT6JATgEEwECACIFAldDN1ACGwMGCwkIBwMCBhUI
|
||||
AgkKCwQWAgMBAh4BAheAAAoJEC9ZtfmbG+C0y7wH/i4xnab36dtrYW7RZwL8i6Sc
|
||||
NjMx4j9+U1kr/F6YtqWd+JwCbBdar5zRghxPcYEq/qf7MbgAYcs1eSOuTOb7n7+o
|
||||
xUwdH2iCtHhKh3Jr2mRw1ks7BbFZPB5KmkxHaEBfLT4d+I91ZuUdPXJ+0SXs9gzk
|
||||
Dbz65Uhoz3W03aiF8HeL5JNARZFMbHHNVL05U1sTGTCOtu+1c/33f3TulQ/XZ3Y4
|
||||
hwGCpLe0Tv7g7Lp3iLMZMWYPEa0a7S4u8he5IEJQLd8bE8jltcQvrdr3Fm8kI2Jg
|
||||
BJmUmX4PSfhuTCFaR/yeCt3UoW883bs9LfbTzIx9DJGpRIu8Y0IL3b4sj/GoZVq5
|
||||
AQ0EV0M3UAEIAKrTaC62ayzqOIPa7nS90BHHck4Z33a2tZF/uof38xNOiyWGhT8u
|
||||
JeFoTTHn5SQq5Ftyu4K3K2fbbpuu/APQF05AaljzVkDGNMW4pSkgOasdysj831cu
|
||||
ssrHX2RYS22wg80k6C/Hwmh5F45faEuNxsV+bPx7oPUrt5n6GMx84vEP3i1+FDBi
|
||||
0pt/B/QnDFBXki1BGvJ35f5NwDefK8VaInxXP3ZN/WIbtn5dqxppkV/YkO7GiJlp
|
||||
Jlju9rf3kKUIQzKQWxFsbCAPIHoWv7rH9RSxgDithXtG6Yg5R1aeBbJaPNXL9wpJ
|
||||
YBJbiMjkAFaz4B95FOqZm3r7oHugiCGsHX0AEQEAAYkBHwQYAQIACQUCV0M3UAIb
|
||||
DAAKCRAvWbX5mxvgtE/OB/0VN88DR3Y3fuqy7lq/dthkn7Dqm9YXdorZl3L152eE
|
||||
IF882aG8FE3qZdaLGjQO4oShAyNWmRfSGuoH0XERXAI9n0r8m4mDMxE6rtP7tHet
|
||||
y/5M8x3CTyuMgx5GLDaEUvBusnTD+/v/fBMwRK/cZ9du5PSG4R50rtst+oYyC2ao
|
||||
x4I2SgjtF/cY7bECsZDplzatN3gv34PkcdIg8SLHAVlL4N5tzumDeizRspcSyoy2
|
||||
K2+hwKU4C4+dekLLTg8rjnRROvplV2KtaEk6rxKtIRFDCoQng8wfJuIMrDNKvqZw
|
||||
FRGt7cbvW5MCnuH8MhItOl9Uxp1wHp6gtav/h8Gp6MBa
|
||||
=MARt
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -11,6 +11,7 @@
|
|||
"stylelint-config-recommended": "^7.0.0",
|
||||
"stylelint-config-standard": "^25.0.0",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"stylelint-config-standard-scss": "^3.0.0",
|
||||
"watch": "^0.13.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -101,6 +101,7 @@ services:
|
|||
build: dev-tools
|
||||
env_file: .env
|
||||
volumes:
|
||||
- /app/dev-tools/
|
||||
- .:/app
|
||||
volumes:
|
||||
pgdata:
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-27 01:11+0000\n"
|
||||
"POT-Creation-Date: 2023-10-02 16:40+0000\n"
|
||||
"PO-Revision-Date: 2021-02-28 17:19-0800\n"
|
||||
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
|
||||
"Language-Team: English <LL@li.org>\n"
|
||||
|
@ -1373,7 +1373,7 @@ msgstr ""
|
|||
|
||||
#: bookwyrm/templates/book/editions/editions.html:8
|
||||
#, python-format
|
||||
msgid "Editions of <a href=\"%(work_path)s\">\"%(work_title)s\"</a>"
|
||||
msgid "Editions of <a href=\"%(work_path)s\"><i>%(work_title)s</i></a>"
|
||||
msgstr ""
|
||||
|
||||
#: bookwyrm/templates/book/editions/editions.html:55
|
||||
|
@ -2806,14 +2806,8 @@ msgstr ""
|
|||
|
||||
#: bookwyrm/templates/import/import.html:21
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" Currently, you are allowed to import %(display_size)s books every %(import_limit_reset)s day.\n"
|
||||
" "
|
||||
msgid_plural ""
|
||||
"\n"
|
||||
" Currently, you are allowed to import %(import_size_limit)s books every %(import_limit_reset)s days.\n"
|
||||
" "
|
||||
msgid "Currently, you are allowed to import %(display_size)s books every %(import_limit_reset)s day."
|
||||
msgid_plural "Currently, you are allowed to import %(import_size_limit)s books every %(import_limit_reset)s days."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
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
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,7 @@ environs==9.5.0
|
|||
flower==1.2.0
|
||||
libsass==0.22.0
|
||||
Markdown==3.4.1
|
||||
Pillow==9.4.0
|
||||
Pillow==10.0.1
|
||||
psycopg2==2.9.5
|
||||
pycryptodome==3.16.0
|
||||
python-dateutil==2.8.2
|
||||
|
|
Loading…
Reference in a new issue