diff --git a/cookbook/migrations/0207_space_logo_color_128_space_logo_color_144_and_more.py b/cookbook/migrations/0207_space_logo_color_128_space_logo_color_144_and_more.py new file mode 100644 index 00000000..6c3adbf5 --- /dev/null +++ b/cookbook/migrations/0207_space_logo_color_128_space_logo_color_144_and_more.py @@ -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'), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index f091b8a4..0614c974 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -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' diff --git a/cookbook/static/assets/favicon-16x16.png b/cookbook/static/assets/favicon-16x16.png deleted file mode 100644 index 2ed29e2c..00000000 Binary files a/cookbook/static/assets/favicon-16x16.png and /dev/null differ diff --git a/cookbook/static/assets/favicon.svg b/cookbook/static/assets/favicon.svg deleted file mode 100644 index bcce5ce8..00000000 --- a/cookbook/static/assets/favicon.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - diff --git a/cookbook/static/assets/safari-pinned-tab.svg b/cookbook/static/assets/logo_black.svg similarity index 100% rename from cookbook/static/assets/safari-pinned-tab.svg rename to cookbook/static/assets/logo_black.svg diff --git a/cookbook/static/assets/logo_color_128.png b/cookbook/static/assets/logo_color_128.png new file mode 100644 index 00000000..544e05c8 Binary files /dev/null and b/cookbook/static/assets/logo_color_128.png differ diff --git a/cookbook/static/assets/logo_color144.png b/cookbook/static/assets/logo_color_144.png similarity index 100% rename from cookbook/static/assets/logo_color144.png rename to cookbook/static/assets/logo_color_144.png diff --git a/cookbook/static/assets/apple-touch-icon.png b/cookbook/static/assets/logo_color_180.png similarity index 100% rename from cookbook/static/assets/apple-touch-icon.png rename to cookbook/static/assets/logo_color_180.png diff --git a/cookbook/static/assets/logo_color_192.png b/cookbook/static/assets/logo_color_192.png new file mode 100644 index 00000000..6ed65212 Binary files /dev/null and b/cookbook/static/assets/logo_color_192.png differ diff --git a/cookbook/static/assets/favicon-32x32.png b/cookbook/static/assets/logo_color_32.png similarity index 100% rename from cookbook/static/assets/favicon-32x32.png rename to cookbook/static/assets/logo_color_32.png diff --git a/cookbook/static/assets/logo_color512.png b/cookbook/static/assets/logo_color_512.png similarity index 100% rename from cookbook/static/assets/logo_color512.png rename to cookbook/static/assets/logo_color_512.png diff --git a/cookbook/static/assets/logo_color.svg b/cookbook/static/assets/logo_color_svg.svg similarity index 100% rename from cookbook/static/assets/logo_color.svg rename to cookbook/static/assets/logo_color_svg.svg diff --git a/cookbook/templates/base.html b/cookbook/templates/base.html index db70ed89..6c5f425d 100644 --- a/cookbook/templates/base.html +++ b/cookbook/templates/base.html @@ -3,6 +3,8 @@ {% load theming_tags %} {% load custom_tags %} +{% theme_values request as theme_values %} + {% 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"/> diff --git a/cookbook/templatetags/theming_tags.py b/cookbook/templatetags/theming_tags.py index 81543e79..6358f016 100644 --- a/cookbook/templatetags/theming_tags.py +++ b/cookbook/templatetags/theming_tags.py @@ -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: diff --git a/cookbook/urls.py b/cookbook/urls.py index 1b1e6548..9566541a 100644 --- a/cookbook/urls.py +++ b/cookbook/urls.py @@ -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 = ( diff --git a/cookbook/views/views.py b/cookbook/views/views.py index dc8d9f10..328ea65e 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -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', {})