From 97e25f26aadb8d368212ded52ab3628a305bc0ae Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 11 Jan 2024 22:55:04 -0800 Subject: [PATCH] App logo --- .../common/logo/logo.component.html | 40 ++++++++++--------- .../components/common/logo/logo.component.ts | 14 +++++++ src-ui/src/app/data/paperless-config.ts | 9 +++++ src-ui/src/app/data/ui-settings.ts | 6 +++ src/documents/views.py | 3 ++ src/paperless/config.py | 2 + ...icationconfiguration_app_logo_and_more.py} | 14 ++++++- src/paperless/models.py | 13 +++++- src/paperless/serialisers.py | 5 +++ src/paperless/settings.py | 2 + src/paperless/urls.py | 9 +++++ 11 files changed, 96 insertions(+), 21 deletions(-) rename src/paperless/migrations/{0002_applicationconfiguration_app_title.py => 0002_applicationconfiguration_app_logo_and_more.py} (52%) diff --git a/src-ui/src/app/components/common/logo/logo.component.html b/src-ui/src/app/components/common/logo/logo.component.html index af08e41fd..6fb003396 100644 --- a/src-ui/src/app/components/common/logo/logo.component.html +++ b/src-ui/src/app/components/common/logo/logo.component.html @@ -1,18 +1,22 @@ - - - - - - - - - - - - - - - - - - +@if (customLogo) { + +} @else { + + + + + + + + + + + + + + + + + + +} diff --git a/src-ui/src/app/components/common/logo/logo.component.ts b/src-ui/src/app/components/common/logo/logo.component.ts index 3320a621a..7404ea865 100644 --- a/src-ui/src/app/components/common/logo/logo.component.ts +++ b/src-ui/src/app/components/common/logo/logo.component.ts @@ -1,4 +1,7 @@ import { Component, Input } from '@angular/core' +import { SETTINGS_KEYS } from 'src/app/data/ui-settings' +import { SettingsService } from 'src/app/services/settings.service' +import { environment } from 'src/environments/environment' @Component({ selector: 'pngx-logo', @@ -12,6 +15,17 @@ export class LogoComponent { @Input() height = '6em' + get customLogo(): string { + return this.settingsService.get(SETTINGS_KEYS.APP_LOGO)?.length + ? environment.apiBaseUrl.replace( + /\/api\/$/, + this.settingsService.get(SETTINGS_KEYS.APP_LOGO) + ) + : null + } + + constructor(private settingsService: SettingsService) {} + getClasses() { return ['logo'].concat(this.extra_classes).join(' ') } diff --git a/src-ui/src/app/data/paperless-config.ts b/src-ui/src/app/data/paperless-config.ts index 6ba3d06cf..1f438f2c2 100644 --- a/src-ui/src/app/data/paperless-config.ts +++ b/src-ui/src/app/data/paperless-config.ts @@ -43,6 +43,7 @@ export enum ConfigOptionType { Select = 'select', Boolean = 'boolean', JSON = 'json', + File = 'file', } export const ConfigCategory = { @@ -165,6 +166,13 @@ export const PaperlessConfigOptions: ConfigOption[] = [ config_key: 'PAPERLESS_OCR_USER_ARGS', category: ConfigCategory.OCR, }, + { + key: 'app_logo', + title: $localize`Application Logo`, + type: ConfigOptionType.File, + config_key: 'PAPERLESS_APP_LOGO', + category: ConfigCategory.General, + }, { key: 'app_title', title: $localize`Application Title`, @@ -188,5 +196,6 @@ export interface PaperlessConfig extends ObjectWithId { max_image_pixels: number color_conversion_strategy: ColorConvertConfig user_args: object + app_logo: string app_title: string } diff --git a/src-ui/src/app/data/ui-settings.ts b/src-ui/src/app/data/ui-settings.ts index f5403acee..e23e490e9 100644 --- a/src-ui/src/app/data/ui-settings.ts +++ b/src-ui/src/app/data/ui-settings.ts @@ -14,6 +14,7 @@ export interface UiSetting { export const SETTINGS_KEYS = { LANGUAGE: 'language', + APP_LOGO: 'app_logo', APP_TITLE: 'app_title', // maintain old general-settings: for backwards compatibility BULK_EDIT_CONFIRMATION_DIALOGS: @@ -195,6 +196,11 @@ export const SETTINGS: UiSetting[] = [ type: 'array', default: [], }, + { + key: SETTINGS_KEYS.APP_LOGO, + type: 'string', + default: '', + }, { key: SETTINGS_KEYS.APP_TITLE, type: 'string', diff --git a/src/documents/views.py b/src/documents/views.py index 230c7ac6d..b545a1466 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1171,6 +1171,9 @@ class UiSettingsView(GenericAPIView): ui_settings["app_title"] = settings.APP_TITLE if general_config.app_title is not None and len(general_config.app_title) > 0: ui_settings["app_title"] = general_config.app_title + ui_settings["app_logo"] = settings.APP_LOGO + if general_config.app_logo is not None and len(general_config.app_logo) > 0: + ui_settings["app_logo"] = general_config.app_logo user_resp = { "id": user.id, diff --git a/src/paperless/config.py b/src/paperless/config.py index f98ed58e1..4195a16db 100644 --- a/src/paperless/config.py +++ b/src/paperless/config.py @@ -102,8 +102,10 @@ class GeneralConfig(BaseConfig): """ app_title: str = dataclasses.field(init=False) + app_logo: str = dataclasses.field(init=False) def __post_init__(self) -> None: app_config = self._get_config_instance() self.app_title = app_config.app_title or None + self.app_logo = app_config.app_logo.url if app_config.app_logo else None diff --git a/src/paperless/migrations/0002_applicationconfiguration_app_title.py b/src/paperless/migrations/0002_applicationconfiguration_app_logo_and_more.py similarity index 52% rename from src/paperless/migrations/0002_applicationconfiguration_app_title.py rename to src/paperless/migrations/0002_applicationconfiguration_app_logo_and_more.py index 09f8885c9..e6960d1a6 100644 --- a/src/paperless/migrations/0002_applicationconfiguration_app_title.py +++ b/src/paperless/migrations/0002_applicationconfiguration_app_logo_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.8 on 2024-01-07 07:31 +# Generated by Django 4.2.9 on 2024-01-12 05:33 from django.db import migrations from django.db import models @@ -10,6 +10,16 @@ class Migration(migrations.Migration): ] operations = [ + migrations.AddField( + model_name="applicationconfiguration", + name="app_logo", + field=models.FileField( + blank=True, + null=True, + upload_to="", + verbose_name="Application logo", + ), + ), migrations.AddField( model_name="applicationconfiguration", name="app_title", @@ -17,7 +27,7 @@ class Migration(migrations.Migration): blank=True, max_length=48, null=True, - verbose_name="Application Title", + verbose_name="Application title", ), ), ] diff --git a/src/paperless/models.py b/src/paperless/models.py index d7bf14f0b..72805dc56 100644 --- a/src/paperless/models.py +++ b/src/paperless/models.py @@ -1,3 +1,4 @@ +from django.core.validators import FileExtensionValidator from django.core.validators import MinValueValidator from django.db import models from django.utils.translation import gettext_lazy as _ @@ -167,12 +168,22 @@ class ApplicationConfiguration(AbstractSingletonModel): ) app_title = models.CharField( - verbose_name=_("Application Title"), + verbose_name=_("Application title"), null=True, blank=True, max_length=48, ) + app_logo = models.FileField( + verbose_name=_("Application logo"), + null=True, + blank=True, + validators=[ + FileExtensionValidator(allowed_extensions=["jpg", "png", "gif", "svg"]), + ], + upload_to="logo/", + ) + class Meta: verbose_name = _("paperless application settings") diff --git a/src/paperless/serialisers.py b/src/paperless/serialisers.py index 50407564b..00065a86b 100644 --- a/src/paperless/serialisers.py +++ b/src/paperless/serialisers.py @@ -129,6 +129,11 @@ class ApplicationConfigurationSerializer(serializers.ModelSerializer): data["user_args"] = None return super().run_validation(data) + def update(self, instance, validated_data): + if instance.app_logo and "app_logo" in validated_data: + instance.app_logo.delete() + return super().update(instance, validated_data) + class Meta: model = ApplicationConfiguration fields = "__all__" diff --git a/src/paperless/settings.py b/src/paperless/settings.py index cddaf4c27..bc815d4d5 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -367,6 +367,7 @@ STORAGES = { "staticfiles": { "BACKEND": _static_backend, }, + "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"}, } _CELERY_REDIS_URL, _CHANNELS_REDIS_URL = _parse_redis_url( @@ -1000,6 +1001,7 @@ if ENABLE_UPDATE_CHECK != "default": ENABLE_UPDATE_CHECK = __get_boolean("PAPERLESS_ENABLE_UPDATE_CHECK") APP_TITLE = os.getenv("PAPERLESS_APP_TITLE", None) +APP_LOGO = os.getenv("PAPERLESS_APP_LOGO", None) ############################################################################### # Machine Learning # diff --git a/src/paperless/urls.py b/src/paperless/urls.py index 25190e0d8..c18e79e8a 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -1,3 +1,5 @@ +import re + from django.conf import settings from django.conf.urls import include from django.contrib import admin @@ -8,6 +10,7 @@ from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic import RedirectView +from django.views.static import serve from rest_framework.authtoken import views from rest_framework.routers import DefaultRouter @@ -181,6 +184,12 @@ urlpatterns = [ url=settings.STATIC_URL + "frontend/en-US/assets/%(path)s", ), ), + # App logo + re_path( + r"^%s(?P.*)$" % re.escape(settings.MEDIA_URL.lstrip("/")), + serve, + kwargs={"document_root": settings.MEDIA_ROOT}, + ), # TODO: with localization, this is even worse! :/ # login, logout path("accounts/", include("django.contrib.auth.urls")),