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")),