mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-23 08:36:32 +00:00
Merge branch 'main' into bump_requirements
This commit is contained in:
commit
7397a2dcb1
8 changed files with 100 additions and 21 deletions
48
bookwyrm/management/commands/compile_themes.py
Normal file
48
bookwyrm/management/commands/compile_themes.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
""" Our own command to all scss themes """
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
import sass
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from sass_processor.apps import APPS_INCLUDE_DIRS
|
||||||
|
from sass_processor.processor import SassProcessor
|
||||||
|
from sass_processor.utils import get_custom_functions
|
||||||
|
|
||||||
|
from bookwyrm import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
"""command-line options"""
|
||||||
|
|
||||||
|
help = "SCSS compile all BookWyrm themes"
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
"""compile"""
|
||||||
|
themes_dir = os.path.join(
|
||||||
|
settings.BASE_DIR, "bookwyrm", "static", "css", "themes", "*.scss"
|
||||||
|
)
|
||||||
|
for theme_scss in glob.glob(themes_dir):
|
||||||
|
basename, _ = os.path.splitext(theme_scss)
|
||||||
|
theme_css = f"{basename}.css"
|
||||||
|
self.compile_sass(theme_scss, theme_css)
|
||||||
|
|
||||||
|
def compile_sass(self, sass_path, css_path):
|
||||||
|
compile_kwargs = {
|
||||||
|
"filename": sass_path,
|
||||||
|
"include_paths": SassProcessor.include_paths + APPS_INCLUDE_DIRS,
|
||||||
|
"custom_functions": get_custom_functions(),
|
||||||
|
"precision": getattr(settings, "SASS_PRECISION", 8),
|
||||||
|
"output_style": getattr(
|
||||||
|
settings,
|
||||||
|
"SASS_OUTPUT_STYLE",
|
||||||
|
"nested" if settings.DEBUG else "compressed",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
content = sass.compile(**compile_kwargs)
|
||||||
|
with open(css_path, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
self.stdout.write("Compiled SASS/SCSS file: '{0}'\n".format(sass_path))
|
|
@ -71,20 +71,29 @@ def get_wrapped_text(text, font, content_width):
|
||||||
low = 0
|
low = 0
|
||||||
high = len(text)
|
high = len(text)
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(Image.new("RGB", (100, 100)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# ideal length is determined via binary search
|
# ideal length is determined via binary search
|
||||||
while low < high:
|
while low < high:
|
||||||
mid = math.floor(low + high)
|
mid = math.floor(low + high)
|
||||||
wrapped_text = textwrap.fill(text, width=mid)
|
wrapped_text = textwrap.fill(text, width=mid)
|
||||||
width = font.getsize_multiline(wrapped_text)[0]
|
|
||||||
|
left, top, right, bottom = draw.multiline_textbbox(
|
||||||
|
(0, 0), wrapped_text, font=font
|
||||||
|
)
|
||||||
|
width = right - left
|
||||||
|
height = bottom - top
|
||||||
|
|
||||||
if width < content_width:
|
if width < content_width:
|
||||||
low = mid
|
low = mid
|
||||||
else:
|
else:
|
||||||
high = mid - 1
|
high = mid - 1
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
wrapped_text = text
|
wrapped_text = text
|
||||||
|
height = 26
|
||||||
|
|
||||||
return wrapped_text
|
return wrapped_text, height
|
||||||
|
|
||||||
|
|
||||||
def generate_texts_layer(texts, content_width):
|
def generate_texts_layer(texts, content_width):
|
||||||
|
@ -100,47 +109,53 @@ def generate_texts_layer(texts, content_width):
|
||||||
text_y = 0
|
text_y = 0
|
||||||
|
|
||||||
if "text_zero" in texts and texts["text_zero"]:
|
if "text_zero" in texts and texts["text_zero"]:
|
||||||
# Text one (Book title)
|
# Text zero (Site preview domain name)
|
||||||
text_zero = get_wrapped_text(texts["text_zero"], font_text_zero, content_width)
|
text_zero, text_height = get_wrapped_text(
|
||||||
|
texts["text_zero"], font_text_zero, content_width
|
||||||
|
)
|
||||||
|
|
||||||
text_layer_draw.multiline_text(
|
text_layer_draw.multiline_text(
|
||||||
(0, text_y), text_zero, font=font_text_zero, fill=TEXT_COLOR
|
(0, text_y), text_zero, font=font_text_zero, fill=TEXT_COLOR
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text_y = text_y + font_text_zero.getsize_multiline(text_zero)[1] + 16
|
text_y = text_y + text_height + 16
|
||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
text_y = text_y + 26
|
text_y = text_y + 26
|
||||||
|
|
||||||
if "text_one" in texts and texts["text_one"]:
|
if "text_one" in texts and texts["text_one"]:
|
||||||
# Text one (Book title)
|
# Text one (Book/Site title, User display name)
|
||||||
text_one = get_wrapped_text(texts["text_one"], font_text_one, content_width)
|
text_one, text_height = get_wrapped_text(
|
||||||
|
texts["text_one"], font_text_one, content_width
|
||||||
|
)
|
||||||
|
|
||||||
text_layer_draw.multiline_text(
|
text_layer_draw.multiline_text(
|
||||||
(0, text_y), text_one, font=font_text_one, fill=TEXT_COLOR
|
(0, text_y), text_one, font=font_text_one, fill=TEXT_COLOR
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text_y = text_y + font_text_one.getsize_multiline(text_one)[1] + 16
|
text_y = text_y + text_height + 16
|
||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
text_y = text_y + 26
|
text_y = text_y + 26
|
||||||
|
|
||||||
if "text_two" in texts and texts["text_two"]:
|
if "text_two" in texts and texts["text_two"]:
|
||||||
# Text one (Book subtitle)
|
# Text two (Book subtitle)
|
||||||
text_two = get_wrapped_text(texts["text_two"], font_text_two, content_width)
|
text_two, text_height = get_wrapped_text(
|
||||||
|
texts["text_two"], font_text_two, content_width
|
||||||
|
)
|
||||||
|
|
||||||
text_layer_draw.multiline_text(
|
text_layer_draw.multiline_text(
|
||||||
(0, text_y), text_two, font=font_text_two, fill=TEXT_COLOR
|
(0, text_y), text_two, font=font_text_two, fill=TEXT_COLOR
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text_y = text_y + font_text_one.getsize_multiline(text_two)[1] + 16
|
text_y = text_y + text_height + 16
|
||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
text_y = text_y + 26
|
text_y = text_y + 26
|
||||||
|
|
||||||
if "text_three" in texts and texts["text_three"]:
|
if "text_three" in texts and texts["text_three"]:
|
||||||
# Text three (Book authors)
|
# Text three (Book authors, Site tagline, User address)
|
||||||
text_three = get_wrapped_text(
|
text_three, _ = get_wrapped_text(
|
||||||
texts["text_three"], font_text_three, content_width
|
texts["text_three"], font_text_three, content_width
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -172,7 +187,7 @@ def generate_instance_layer(content_width):
|
||||||
instance_text_x = 0
|
instance_text_x = 0
|
||||||
|
|
||||||
if logo_img:
|
if logo_img:
|
||||||
logo_img.thumbnail((50, 50), Image.ANTIALIAS)
|
logo_img.thumbnail((50, 50), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
instance_layer.paste(logo_img, (0, 0))
|
instance_layer.paste(logo_img, (0, 0))
|
||||||
|
|
||||||
|
@ -183,7 +198,7 @@ def generate_instance_layer(content_width):
|
||||||
(instance_text_x, 10), site.name, font=font_instance, fill=TEXT_COLOR
|
(instance_text_x, 10), site.name, font=font_instance, fill=TEXT_COLOR
|
||||||
)
|
)
|
||||||
|
|
||||||
line_width = 50 + 10 + font_instance.getsize(site.name)[0]
|
line_width = 50 + 10 + round(font_instance.getlength(site.name))
|
||||||
|
|
||||||
line_layer = Image.new(
|
line_layer = Image.new(
|
||||||
"RGBA", (line_width, 2), color=(*(ImageColor.getrgb(TEXT_COLOR)), 50)
|
"RGBA", (line_width, 2), color=(*(ImageColor.getrgb(TEXT_COLOR)), 50)
|
||||||
|
@ -253,10 +268,12 @@ def generate_default_inner_img():
|
||||||
default_cover_draw = ImageDraw.Draw(default_cover)
|
default_cover_draw = ImageDraw.Draw(default_cover)
|
||||||
|
|
||||||
text = "no image :("
|
text = "no image :("
|
||||||
text_dimensions = font_cover.getsize(text)
|
text_left, text_top, text_right, text_bottom = font_cover.getbbox(text)
|
||||||
|
text_width, text_height = text_right - text_left, text_bottom - text_top
|
||||||
|
|
||||||
text_coords = (
|
text_coords = (
|
||||||
math.floor((inner_img_width - text_dimensions[0]) / 2),
|
math.floor((inner_img_width - text_width) / 2),
|
||||||
math.floor((inner_img_height - text_dimensions[1]) / 2),
|
math.floor((inner_img_height - text_height) / 2),
|
||||||
)
|
)
|
||||||
default_cover_draw.text(text_coords, text, font=font_cover, fill="white")
|
default_cover_draw.text(text_coords, text, font=font_cover, fill="white")
|
||||||
|
|
||||||
|
@ -273,7 +290,9 @@ def generate_preview_image(
|
||||||
# Cover
|
# Cover
|
||||||
try:
|
try:
|
||||||
inner_img_layer = Image.open(picture)
|
inner_img_layer = Image.open(picture)
|
||||||
inner_img_layer.thumbnail((inner_img_width, inner_img_height), Image.ANTIALIAS)
|
inner_img_layer.thumbnail(
|
||||||
|
(inner_img_width, inner_img_height), Image.Resampling.LANCZOS
|
||||||
|
)
|
||||||
color_thief = ColorThief(picture)
|
color_thief = ColorThief(picture)
|
||||||
dominant_color = color_thief.get_color(quality=1)
|
dominant_color = color_thief.get_color(quality=1)
|
||||||
except: # pylint: disable=bare-except
|
except: # pylint: disable=bare-except
|
||||||
|
|
|
@ -193,7 +193,8 @@ STATICFILES_FINDERS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
SASS_PROCESSOR_INCLUDE_FILE_PATTERN = r"^.+\.[s]{0,1}(?:a|c)ss$"
|
SASS_PROCESSOR_INCLUDE_FILE_PATTERN = r"^.+\.[s]{0,1}(?:a|c)ss$"
|
||||||
SASS_PROCESSOR_ENABLED = True
|
# when debug is disabled, make sure to compile themes once with `./bw-dev compile_themes`
|
||||||
|
SASS_PROCESSOR_ENABLED = DEBUG
|
||||||
|
|
||||||
# minify css is production but not dev
|
# minify css is production but not dev
|
||||||
if not DEBUG:
|
if not DEBUG:
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
{% trans "Copy the theme file into the <code>bookwyrm/static/css/themes</code> directory on your server from the command line." %}
|
{% trans "Copy the theme file into the <code>bookwyrm/static/css/themes</code> directory on your server from the command line." %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{% trans "Run <code>./bw-dev collectstatic</code>." %}
|
{% trans "Run <code>./bw-dev compile_themes</code> and <code>./bw-dev collectstatic</code>." %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{% trans "Add the file name using the form below to make it available in the application interface." %}
|
{% trans "Add the file name using the form below to make it available in the application interface." %}
|
||||||
|
|
7
bw-dev
7
bw-dev
|
@ -92,6 +92,7 @@ case "$CMD" in
|
||||||
migrate
|
migrate
|
||||||
migrate django_celery_beat
|
migrate django_celery_beat
|
||||||
initdb
|
initdb
|
||||||
|
runweb python manage.py compile_themes
|
||||||
runweb python manage.py collectstatic --no-input
|
runweb python manage.py collectstatic --no-input
|
||||||
admin_code
|
admin_code
|
||||||
;;
|
;;
|
||||||
|
@ -122,6 +123,9 @@ case "$CMD" in
|
||||||
prod_error
|
prod_error
|
||||||
runweb pytest -n 3 --cov-report term-missing "$@"
|
runweb pytest -n 3 --cov-report term-missing "$@"
|
||||||
;;
|
;;
|
||||||
|
compile_themes)
|
||||||
|
runweb python manage.py compile_themes
|
||||||
|
;;
|
||||||
collectstatic)
|
collectstatic)
|
||||||
runweb python manage.py collectstatic --no-input
|
runweb python manage.py collectstatic --no-input
|
||||||
;;
|
;;
|
||||||
|
@ -203,6 +207,7 @@ case "$CMD" in
|
||||||
docker-compose build
|
docker-compose build
|
||||||
# ./update.sh
|
# ./update.sh
|
||||||
runweb python manage.py migrate
|
runweb python manage.py migrate
|
||||||
|
runweb python manage.py compile_themes
|
||||||
runweb python manage.py collectstatic --no-input
|
runweb python manage.py collectstatic --no-input
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
docker-compose restart web
|
docker-compose restart web
|
||||||
|
@ -256,6 +261,7 @@ case "$CMD" in
|
||||||
migrate
|
migrate
|
||||||
migrate django_celery_beat
|
migrate django_celery_beat
|
||||||
initdb
|
initdb
|
||||||
|
runweb python manage.py compile_themes
|
||||||
runweb python manage.py collectstatic --no-input
|
runweb python manage.py collectstatic --no-input
|
||||||
admin_code
|
admin_code
|
||||||
;;
|
;;
|
||||||
|
@ -283,6 +289,7 @@ case "$CMD" in
|
||||||
echo " dbshell"
|
echo " dbshell"
|
||||||
echo " restart_celery"
|
echo " restart_celery"
|
||||||
echo " pytest [path]"
|
echo " pytest [path]"
|
||||||
|
echo " compile_themes"
|
||||||
echo " collectstatic"
|
echo " collectstatic"
|
||||||
echo " makemessages"
|
echo " makemessages"
|
||||||
echo " compilemessages [locale]"
|
echo " compilemessages [locale]"
|
||||||
|
|
|
@ -14,6 +14,7 @@ dbshell \
|
||||||
restart_celery \
|
restart_celery \
|
||||||
pytest \
|
pytest \
|
||||||
pytest_coverage_report \
|
pytest_coverage_report \
|
||||||
|
compile_themes \
|
||||||
collectstatic \
|
collectstatic \
|
||||||
makemessages \
|
makemessages \
|
||||||
compilemessages \
|
compilemessages \
|
||||||
|
@ -54,6 +55,7 @@ __bw_complete "$commands" "shell" "open the Python shell withi
|
||||||
__bw_complete "$commands" "dbshell" "open the database shell within the web container"
|
__bw_complete "$commands" "dbshell" "open the database shell within the web container"
|
||||||
__bw_complete "$commands" "restart_celery" "restart the celery container"
|
__bw_complete "$commands" "restart_celery" "restart the celery container"
|
||||||
__bw_complete "$commands" "pytest" "run unit tests"
|
__bw_complete "$commands" "pytest" "run unit tests"
|
||||||
|
__bw_complete "$commands" "compile_themes" "compile themes css files"
|
||||||
__bw_complete "$commands" "collectstatic" "copy changed static files into the installation"
|
__bw_complete "$commands" "collectstatic" "copy changed static files into the installation"
|
||||||
__bw_complete "$commands" "makemessages" "extract all localizable messages from the code"
|
__bw_complete "$commands" "makemessages" "extract all localizable messages from the code"
|
||||||
__bw_complete "$commands" "compilemessages" "compile .po localization files to .mo"
|
__bw_complete "$commands" "compilemessages" "compile .po localization files to .mo"
|
||||||
|
|
|
@ -11,6 +11,7 @@ dbshell
|
||||||
restart_celery
|
restart_celery
|
||||||
pytest
|
pytest
|
||||||
pytest_coverage_report
|
pytest_coverage_report
|
||||||
|
compile_themes
|
||||||
collectstatic
|
collectstatic
|
||||||
makemessages
|
makemessages
|
||||||
compilemessages
|
compilemessages
|
||||||
|
|
|
@ -13,6 +13,7 @@ dbshell
|
||||||
restart_celery
|
restart_celery
|
||||||
pytest
|
pytest
|
||||||
pytest_coverage_report
|
pytest_coverage_report
|
||||||
|
compile_themes
|
||||||
collectstatic
|
collectstatic
|
||||||
makemessages
|
makemessages
|
||||||
compilemessages
|
compilemessages
|
||||||
|
|
Loading…
Reference in a new issue