basics of custom icons

This commit is contained in:
vabene1111 2024-01-06 23:23:17 +08:00
parent 50e1eaf645
commit c6fa635af2
16 changed files with 173 additions and 64 deletions

View File

@ -0,0 +1,49 @@
# Generated by Django 4.2.7 on 2024-01-06 15:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0206_rename_sticky_navbar_userpreference_nav_sticky_and_more'),
]
operations = [
migrations.AddField(
model_name='space',
name='logo_color_128',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_logo_color_128', to='cookbook.userfile'),
),
migrations.AddField(
model_name='space',
name='logo_color_144',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_logo_color_144', to='cookbook.userfile'),
),
migrations.AddField(
model_name='space',
name='logo_color_180',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_logo_color_180', to='cookbook.userfile'),
),
migrations.AddField(
model_name='space',
name='logo_color_192',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_logo_color_192', to='cookbook.userfile'),
),
migrations.AddField(
model_name='space',
name='logo_color_32',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_logo_color_32', to='cookbook.userfile'),
),
migrations.AddField(
model_name='space',
name='logo_color_512',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_logo_color_512', to='cookbook.userfile'),
),
migrations.AddField(
model_name='space',
name='logo_color_svg',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_logo_color_svg', to='cookbook.userfile'),
),
]

View File

@ -289,6 +289,14 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
nav_bg_color = models.CharField(max_length=8, default='', blank=True, )
nav_text_color = models.CharField(max_length=16, choices=NAV_TEXT_COLORS, default=BLANK)
logo_color_32 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_32')
logo_color_128 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_128')
logo_color_144 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_144')
logo_color_180 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_180')
logo_color_192 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_192')
logo_color_512 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_512')
logo_color_svg = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_svg')
created_by = models.ForeignKey(User, on_delete=models.PROTECT, null=True)
created_at = models.DateTimeField(auto_now_add=True)
message = models.CharField(max_length=512, default='', blank=True)
@ -1344,6 +1352,9 @@ class UserFile(ExportModelOperationsMixin('user_files'), models.Model, Permissio
self.file_size_kb = round(self.file.size / 1000)
super(UserFile, self).save(*args, **kwargs)
def __str__(self):
return f'{self.name} (#{self.id})'
class Automation(ExportModelOperationsMixin('automations'), models.Model, PermissionModelMixin):
FOOD_ALIAS = 'FOOD_ALIAS'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,41 +0,0 @@
<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Logo" transform="matrix(0.637323,0,0,0.637323,-243.095,-716.725)">
<g id="Kreis" transform="matrix(1.44936,0,0,1.50279,387.258,1039.34)">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584" style="fill:url(#_Linear1);"/>
<clipPath id="_clip2">
<ellipse cx="273.123" cy="324.015" rx="259.822" ry="250.584"/>
</clipPath>
<g clip-path="url(#_clip2)">
<g id="Shadow" transform="matrix(1.10322,0,0,1.064,-5.58287,50.5786)">
<path d="M156.285,427.208L389.554,660.477L668.803,495.551L374.012,200.761L156.285,427.208Z" style="fill:rgb(22,22,22);"/>
<g transform="matrix(1,0,0,1,-4.22105,0.775864)">
<path d="M208.628,178.613L485.935,455.919L590.027,364.63L296.923,71.526L294.175,138.989L208.628,178.613Z" style="fill:rgb(22,22,22);"/>
</g>
<g transform="matrix(1,0,0,1,-85.3876,27.8512)">
<path d="M310.385,145.641L587.692,422.948L590.392,361.357L297.288,68.253L294.175,138.989L310.385,145.641Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
</g>
</g>
<g transform="matrix(1.471,0,0,1.471,406.537,1149.69)">
<path d="M256.049,220C286.222,219.994 312.656,207.31 329.388,194.134C346.35,180.754 370.899,183.406 384.611,200.1C407.129,227.376 420.598,261.944 420.598,299.53C420.598,361.08 382.604,437.101 329.764,463.706C307.035,475.15 283.466,480.586 256.098,480.599L256.098,480.599L256.049,480.599L256,480.599L256,480.599C228.632,480.586 205.063,475.15 182.334,463.706C129.494,437.101 91.5,361.08 91.5,299.53C91.5,261.944 104.969,227.376 127.487,200.1C141.199,183.406 165.748,180.754 182.71,194.134C199.442,207.31 225.876,219.994 256.049,220Z" style="fill:rgb(255,203,118);"/>
</g>
<g id="Flame-2" transform="matrix(0.965725,0,0,0.89175,164.497,436.391)">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z" style="fill:rgb(255,111,0);"/>
<clipPath id="_clip3">
<path d="M604.408,844.314C601.981,840.845 601.962,836.056 604.362,832.565C606.763,829.074 611.005,827.721 614.769,829.246C633.87,836.869 658.833,848.629 678.207,864.452C718.526,897.381 729.55,919.407 738.552,942.091C749.208,968.943 750.785,996.68 748.515,1016.08C742.018,1071.61 700.355,1117.5 641.034,1117.5C581.713,1117.5 534.493,1072.05 533.553,1016.08C532.986,982.372 543.985,955.443 555.988,936.22C558.982,931.437 564.594,929.469 569.609,931.444C574.623,933.419 577.757,938.831 577.215,944.58C575.493,956.716 574.362,969.372 574.932,979.484C576.863,1013.7 597.171,1022.5 618.083,1022.29C640.371,1022.08 662.925,1003.17 654.797,954.895C647.69,912.681 622.362,870.194 604.408,844.314Z"/>
</clipPath>
<g clip-path="url(#_clip3)">
<g transform="matrix(1.28784,-0.270602,0.285942,1.59598,247.349,825.209)">
<path d="M255.004,46.957C279.547,58.545 306,85.447 313.307,120.161C325.437,177.791 291.571,193.789 262.496,192.403C215.889,190.181 200.194,153.246 231.326,108.9C250.631,81.401 232.663,36.408 255.004,46.957Z" style="fill:rgb(255,209,0);"/>
</g>
</g>
</g>
<g id="Hut" transform="matrix(1.521,0,0,1.521,393.566,1149.06)">
<path d="M228.197,408.524C222.698,408.524 217.813,406.688 214.024,403.619C211.776,401.794 210.92,398.752 211.888,396.024C212.856,393.295 215.437,391.472 218.332,391.472C232.214,391.4 256.112,391.396 256.112,391.396C256.112,391.396 280.009,391.4 293.891,391.472C296.786,391.472 299.367,393.295 300.335,396.024C301.303,398.752 300.447,401.794 298.199,403.619C294.41,406.688 289.526,408.524 284.027,408.524L228.197,408.524ZM217.24,378.877C214.208,378.877 211.3,377.671 209.158,375.525C207.015,373.379 205.814,370.469 205.82,367.436C205.831,361.119 205.842,354.539 205.842,354.539C205.842,350.423 203.097,346.814 199.131,345.714C185.313,341.841 175.2,329.468 175.2,314.823C175.2,297.07 190.059,282.657 208.362,282.657C208.362,282.657 208.362,282.657 208.362,282.657C215.401,282.657 221.675,278.218 224.017,271.581C227.243,262.39 236.411,252.015 256,251.998L256,251.998L256.223,251.998L256.223,251.998C275.812,252.015 284.98,262.39 288.206,271.581C290.549,278.218 296.822,282.657 303.861,282.657C303.861,282.657 303.861,282.657 303.861,282.657C322.164,282.657 337.023,297.07 337.023,314.823C337.023,329.468 326.911,341.841 313.093,345.714C309.127,346.814 306.382,350.423 306.381,354.539C306.381,354.539 306.386,361.127 306.391,367.447C306.394,370.478 305.191,373.385 303.049,375.529C300.907,377.672 298.001,378.877 294.971,378.877C275.615,378.877 236.604,378.877 217.24,378.877Z" style="fill:rgb(22,22,22);"/>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2e-06,0,0,2e-06,3755.77,81.7179)"><stop offset="0" style="stop-color:rgb(39,39,39);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(108,108,108);stop-opacity:1"/></linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -3,6 +3,8 @@
{% load theming_tags %}
{% load custom_tags %}
{% theme_values request as theme_values %}
<html>
<head>
<title>{% block title %}
@ -11,23 +13,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="noindex,nofollow"/>
<link rel="shortcut icon" type="image/x-icon" href="{% static 'assets/favicon.svg' %}">
<link rel="shortcut icon" href="{% static 'assets/favicon.svg' %}">
<link rel="icon" type="image/png" href="{% static 'assets/favicon-32x32.png' %}" sizes="32x32">
<link rel="icon" type="image/png" href="{% static 'assets/favicon-16x16.png' %}" sizes="16x16">
<link rel="mask-icon" href="{% static 'assets/safari-pinned-tab.svg' %}" color="#161616">
<link rel="apple-touch-icon" href="{% static 'assets/apple-touch-icon.png' %}" sizes="180x180">
<link rel="icon" href="{{ theme_values.logo_color_svg }}">
<link rel="icon" href="{{ theme_values.logo_color_32 }}" sizes="32x32">
<link rel="icon" href="{{ theme_values.logo_color_128 }}" sizes="128x128">
<link rel="icon" href="{{ theme_values.logo_color_192 }}" sizes="192x192">
<link rel="apple-touch-icon" href="{{ theme_values.logo_color_180 }}" sizes="180x180">
<link rel="manifest" crossorigin="use-credentials" href="{% url 'web_manifest' %}">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<meta name="msapplication-TileColor" content="{% nav_bg_color request %}">
<meta name="msapplication-TileImage" content="{{ theme_values.logo_color_144 }}">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#161616">
<meta name="msapplication-TileColor" content="#161616">
<meta name="theme-color" content="#ffffff">
<meta name="theme-color" content="{% nav_bg_color request %}">
<meta name="apple-mobile-web-app-capable" content="yes"/>

View File

@ -8,6 +8,33 @@ from recipes.settings import STICKY_NAV_PREF_DEFAULT, UNAUTHENTICATED_THEME_FROM
register = template.Library()
@register.simple_tag
def theme_values(request):
# TODO move all theming values to this tag to prevent double queries
tv = {
'logo_color_32': static('assets/logo_color_32.png'),
'logo_color_128': static('assets/logo_color_128.png'),
'logo_color_144': static('assets/logo_color_144.png'),
'logo_color_180': static('assets/logo_color_180.png'),
'logo_color_192': static('assets/logo_color_192.png'),
'logo_color_512': static('assets/logo_color_512.png'),
'logo_color_svg': static('assets/logo_color_svg.svg'),
}
space = None
if request.space:
space = request.space
if UNAUTHENTICATED_THEME_FROM_SPACE > 0: # TODO load unauth space setting on boot in settings.py and use them here
with scopes_disabled():
space = Space.objects.filter(id=UNAUTHENTICATED_THEME_FROM_SPACE).first()
for logo in list(tv.keys()):
print(f'looking for {logo} in {space} has logo {getattr(space, logo, None)}')
if logo.startswith('logo_color_') and getattr(space, logo, None):
tv[logo] = getattr(space, logo).file.url
return tv
@register.simple_tag
def theme_url(request):
themes = {
@ -20,7 +47,7 @@ def theme_url(request):
}
if not request.user.is_authenticated:
if UNAUTHENTICATED_THEME_FROM_SPACE > 0: # TODO load unauth space setting on boot in settings.py and use them here
if UNAUTHENTICATED_THEME_FROM_SPACE > 0: # TODO load unauth space setting on boot in settings.py and use them here
with scopes_disabled():
return static(themes[Space.objects.filter(id=UNAUTHENTICATED_THEME_FROM_SPACE).first().space_theme])
else:

View File

@ -162,8 +162,7 @@ urlpatterns = [
path('service-worker.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript', )),
name='service_worker'),
path('manifest.json', (TemplateView.as_view(template_name="manifest.json", content_type='application/json', )),
name='web_manifest'),
path('manifest.json', views.web_manifest, name='web_manifest'),
]
generic_models = (

View File

@ -1,3 +1,4 @@
import json
import os
import re
from datetime import datetime
@ -14,8 +15,9 @@ from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
from django.core.management import call_command
from django.db import models
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.templatetags.static import static
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from django.utils.translation import gettext as _
@ -335,13 +337,16 @@ def system(request):
database_message = _('Everything is fine!')
elif postgres_ver < postgres_current - 2:
database_status = 'danger'
database_message = _('PostgreSQL %(v)s is deprecated. Upgrade to a fully supported version!') % {'v': postgres_ver}
database_message = _('PostgreSQL %(v)s is deprecated. Upgrade to a fully supported version!') % {
'v': postgres_ver}
else:
database_status = 'info'
database_message = _('You are running PostgreSQL %(v1)s. PostgreSQL %(v2)s is recommended') % {'v1': postgres_ver, 'v2': postgres_current}
database_message = _('You are running PostgreSQL %(v1)s. PostgreSQL %(v2)s is recommended') % {
'v1': postgres_ver, 'v2': postgres_current}
else:
database_status = 'info'
database_message = _('This application is not running with a Postgres database backend. This is ok but not recommended as some features only work with postgres databases.')
database_message = _(
'This application is not running with a Postgres database backend. This is ok but not recommended as some features only work with postgres databases.')
secret_key = False if os.getenv('SECRET_KEY') else True
@ -366,10 +371,12 @@ def system(request):
pass
else:
current_app = row
migration_info[current_app] = {'app': current_app, 'unapplied_migrations': [], 'applied_migrations': [], 'total': 0}
migration_info[current_app] = {'app': current_app, 'unapplied_migrations': [], 'applied_migrations': [],
'total': 0}
for key in migration_info.keys():
migration_info[key]['total'] = len(migration_info[key]['unapplied_migrations']) + len(migration_info[key]['applied_migrations'])
migration_info[key]['total'] = len(migration_info[key]['unapplied_migrations']) + len(
migration_info[key]['applied_migrations'])
return render(request, 'system.html', {
'gunicorn_media': settings.GUNICORN_MEDIA,
@ -431,7 +438,8 @@ def invite_link(request, token):
link.used_by = request.user
link.save()
user_space = UserSpace.objects.create(user=request.user, space=link.space, internal_note=link.internal_note, invite_link=link, active=False)
user_space = UserSpace.objects.create(user=request.user, space=link.space,
internal_note=link.internal_note, invite_link=link, active=False)
if request.user.userspace_set.count() == 1:
user_space.active = True
@ -472,6 +480,65 @@ def report_share_abuse(request, token):
return HttpResponseRedirect(reverse('index'))
def web_manifest(request):
icons = [
{"src": static("/assets/logo_color.svg"), "sizes": "any"},
{"src": static("/assets/logo_color144.png"), "type": "image/png", "sizes": "144x144"},
{"src": static("/assets/logo_color512.png"), "type": "image/png", "sizes": "512x512"}
]
if request.user.is_authenticated and getattr(request.space, 'logo_color_svg') and getattr(request.space, 'logo_color_144') and getattr(request.space, 'logo_color_512'):
icons = [
{"src": request.space.logo_color_svg.file.url, "sizes": "any"},
{"src": request.space.logo_color_144.file.url, "type": "image/png", "sizes": "144x144"},
{"src": request.space.logo_color_512.file.url, "type": "image/png", "sizes": "512x512"}
]
manifest_info = {
"name": "Tandoor Recipes",
"short_name": "Tandoor",
"description": _("Manage recipes, shopping list, meal plans and more."),
"icons": icons,
"start_url": "./search",
"background_color": "#ffcb76",
"display": "standalone",
"scope": ".",
"theme_color": "#ffcb76",
"shortcuts": [
{
"name": _("Plan"),
"short_name": _("Plan"),
"description": _("View your meal Plan"),
"url": "./plan"
},
{
"name": _("Books"),
"short_name": _("Books"),
"description": _("View your cookbooks"),
"url": "./books"
},
{
"name": _("Shopping"),
"short_name": _("Shopping"),
"description": _("View your shopping lists"),
"url": "./list/shopping-list/"
}
],
"share_target": {
"action": "/data/import/url",
"method": "GET",
"params": {
"title": "title",
"url": "url",
"text": "text"
}
}
}
return JsonResponse(manifest_info, json_dumps_params={'indent': 4})
def markdown_info(request):
return render(request, 'markdown_info.html', {})