diff --git a/app/activitypub/util.py b/app/activitypub/util.py index 210bff8e..f39bb357 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -2183,6 +2183,7 @@ def parse_redis_socket_string(connection_string: str): def lemmy_site_data(): site = g.site + logo = site.logo if site.logo else '/static/images/logo2.png' data = { "site_view": { "site": { @@ -2191,7 +2192,7 @@ def lemmy_site_data(): "sidebar": site.sidebar, "published": site.created_at.isoformat(), "updated": site.updated.isoformat(), - "icon": f"https://{current_app.config['SERVER_NAME']}/static/images/logo2.png", + "icon": f"https://{current_app.config['SERVER_NAME']}{logo}", "banner": "", "description": site.description, "actor_id": f"https://{current_app.config['SERVER_NAME']}/", diff --git a/app/admin/routes.py b/app/admin/routes.py index 54eef10b..94ee0d6c 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -1,11 +1,14 @@ +import os from datetime import datetime, timedelta +from io import BytesIO from time import sleep -from flask import request, flash, json, url_for, current_app, redirect, g +from flask import request, flash, json, url_for, current_app, redirect, g, abort from flask_login import login_required, current_user from flask_babel import _ from slugify import slugify from sqlalchemy import text, desc, or_ +from PIL import Image from app import db, celery, cache from app.activitypub.routes import process_inbox_request, process_delete_request @@ -21,7 +24,7 @@ from app.models import AllowedInstances, BannedInstances, ActivityPubLog, utcnow User, Instance, File, Report, Topic, UserRegistration, Role, Post, PostReply, Language from app.utils import render_template, permission_required, set_setting, get_setting, gibberish, markdown_to_html, \ moderating_communities, joined_communities, finalize_user_setup, theme_list, blocked_phrases, blocked_referrers, \ - topic_tree, languages_for_form, menu_topics + topic_tree, languages_for_form, menu_topics, ensure_directory_exists from app.admin import bp @@ -53,6 +56,58 @@ def admin_site(): site.contact_email = form.contact_email.data if site.id is None: db.session.add(site) + # Save site icon + uploaded_icon = request.files['icon'] + if uploaded_icon and uploaded_icon.filename != '': + allowed_extensions = ['.gif', '.jpg', '.jpeg', '.png', '.webp'] + file_ext = os.path.splitext(uploaded_icon.filename)[1] + if file_ext.lower() not in allowed_extensions: + abort(400) + directory = 'app/static/media' + ensure_directory_exists(directory) + + # Remove existing logo files + if os.path.isfile(f'app{site.logo}'): + os.unlink(f'app{site.logo}') + if os.path.isfile(f'app{site.logo_152}'): + os.unlink(f'app{site.logo_152}') + if os.path.isfile(f'app{site.logo_32}'): + os.unlink(f'app{site.logo_32}') + if os.path.isfile(f'app{site.logo_16}'): + os.unlink(f'app{site.logo_16}') + + # Save logo file + base_filename = f'logo_{gibberish(5)}' + uploaded_icon.save(f'{directory}/{base_filename}{file_ext}') + img = Image.open(f'{directory}/{base_filename}{file_ext}') + if img.width > 100: + img.thumbnail((100, 100)) + img.save(f'{directory}/{base_filename}_100{file_ext}') + site.logo = f'/static/media/{base_filename}_100{file_ext}' + delete_original = True + else: + site.logo = f'/static/media/{base_filename}{file_ext}' + delete_original = False + + # Save multiple copies of the logo - different sizes + img = Image.open(f'{directory}/{base_filename}{file_ext}') + img.thumbnail((152, 152)) + img.save(f'{directory}/{base_filename}_152{file_ext}') + site.logo_152 = f'/static/media/{base_filename}_152{file_ext}' + + img = Image.open(f'{directory}/{base_filename}{file_ext}') + img.thumbnail((32, 32)) + img.save(f'{directory}/{base_filename}_32{file_ext}') + site.logo_32 = f'/static/media/{base_filename}_32{file_ext}' + + img = Image.open(f'{directory}/{base_filename}{file_ext}') + img.thumbnail((16, 16)) + img.save(f'{directory}/{base_filename}_16{file_ext}') + site.logo_16 = f'/static/media/{base_filename}_16{file_ext}' + + if delete_original: + os.unlink(f'app/static/media/{base_filename}{file_ext}') + db.session.commit() set_setting('announcement', form.announcement.data) flash('Settings saved.') diff --git a/app/models.py b/app/models.py index 59906c2e..e4376269 100644 --- a/app/models.py +++ b/app/models.py @@ -1479,6 +1479,10 @@ class Site(db.Model): default_theme = db.Column(db.String(20), default='') contact_email = db.Column(db.String(255), default='') about = db.Column(db.Text, default='') + logo = db.Column(db.String(40), default='') + logo_152 = db.Column(db.String(40), default='') + logo_32 = db.Column(db.String(40), default='') + logo_16 = db.Column(db.String(40), default='') @staticmethod def admins() -> List[User]: diff --git a/app/templates/admin/site.html b/app/templates/admin/site.html index e2841815..73f70799 100644 --- a/app/templates/admin/site.html +++ b/app/templates/admin/site.html @@ -17,17 +17,17 @@ {{ render_field(form.name) }} {{ render_field(form.description) }} {{ render_field(form.sidebar) }} -
HTML is allowed in this field.
+{{ _('HTML is allowed in this field.') }}
{{ render_field(form.announcement) }} -HTML is allowed in this field.
+{{ _('HTML is allowed in this field.') }}
{{ render_field(form.icon) }} - +{{ _('Provide a square image, minimum 50 pixels wide but ideally 152+ pixels.') }}
{{ _('Provide a more extensive description of the instance, set a contact address and provide legal information. This information appears on the about page.') }}
{{ render_field(form.about) }} -HTML is allowed in this field.
+{{ _('HTML is allowed in this field.') }}
{{ render_field(form.legal_information) }} -HTML is allowed in this field.
+{{ _('HTML is allowed in this field.') }}
{{ render_field(form.contact_email) }} {{ render_field(form.submit) }} diff --git a/app/templates/base.html b/app/templates/base.html index 65ce463e..686b90c9 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -53,12 +53,12 @@ {% endif -%} {% endblock -%}