theming refactor

moved server side for a better page loading experience and less javascript mess
This commit is contained in:
vabene1111
2020-02-13 23:47:24 +01:00
parent c08e30c5a9
commit cc7422a503
10 changed files with 160 additions and 80 deletions

View File

@ -26,6 +26,12 @@ class DateWidget(forms.DateInput):
super().__init__(**kwargs) super().__init__(**kwargs)
class UserPreferenceForm(forms.ModelForm):
class Meta:
model = UserPreference
fields = ('theme',)
class ExternalRecipeForm(forms.ModelForm): class ExternalRecipeForm(forms.ModelForm):
file_path = forms.CharField(disabled=True, required=False) file_path = forms.CharField(disabled=True, required=False)
storage = forms.ModelChoiceField(queryset=Storage.objects.all(), disabled=True, required=False) storage = forms.ModelChoiceField(queryset=Storage.objects.all(), disabled=True, required=False)

View File

@ -0,0 +1,24 @@
# Generated by Django 3.0.2 on 2020-02-13 22:15
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0012_auto_20200130_1116'),
]
operations = [
migrations.CreateModel(
name='UserPreference',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('theme', models.CharField(choices=[('BOOTSTRAP', 'Bootstrap'), ('DARKLY', 'Darkly'), ('FLATLY', 'Flatly')], default='BOOTSTRAP', max_length=128)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 3.0.2 on 2020-02-13 22:32
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0013_userpreference'),
]
operations = [
migrations.AlterField(
model_name='userpreference',
name='theme',
field=models.CharField(choices=[('BOOTSTRAP', 'Bootstrap'), ('DARKLY', 'Darkly'), ('FLATLY', 'Flatly'), ('SUPERHERO', 'Superhero')], default='BOOTSTRAP', max_length=128),
),
migrations.AlterField(
model_name='userpreference',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, unique=True),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 3.0.2 on 2020-02-13 22:34
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0014_auto_20200213_2332'),
]
operations = [
migrations.AlterField(
model_name='userpreference',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.0.2 on 2020-02-13 22:35
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0015_auto_20200213_2334'),
]
operations = [
migrations.RemoveField(
model_name='userpreference',
name='id',
),
migrations.AlterField(
model_name='userpreference',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -3,6 +3,18 @@ from django.utils.translation import gettext as _
from django.db import models from django.db import models
class UserPreference(models.Model):
BOOTSTRAP = 'BOOTSTRAP'
DARKLY = 'DARKLY'
FLATLY = 'FLATLY'
SUPERHERO = 'SUPERHERO'
THEMES = ((BOOTSTRAP, 'Bootstrap'), (DARKLY, 'Darkly'), (FLATLY, 'Flatly'), (SUPERHERO, 'Superhero'))
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
theme = models.CharField(choices=THEMES, max_length=128, default=BOOTSTRAP)
class Storage(models.Model): class Storage(models.Model):
DROPBOX = 'DB' DROPBOX = 'DB'
NEXTCLOUD = 'NEXTCLOUD' NEXTCLOUD = 'NEXTCLOUD'

View File

@ -1,5 +1,6 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% load custom_tags %}
<html> <html>
<head> <head>
@ -18,31 +19,11 @@
<meta name="msapplication-TileImage" content="/mstile-144x144.png"> <meta name="msapplication-TileImage" content="/mstile-144x144.png">
<!-- Bootstrap 4 --> <!-- Bootstrap 4 -->
<link id="id_main_css" rel="stylesheet" <link id="id_main_css" href="{% theme_url request %}" rel="stylesheet">
href="{% static 'themes/darkly.min.css' %}">
<script src="https://code.jquery.com/jquery-3.4.1.js" <script src="https://code.jquery.com/jquery-3.4.1.js"
integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script type="text/javascript">
function getCookieValue(a) {
let b = document.cookie.match('(^|[^;]+)\\s*' + a + '\\s*=\\s*([^;]+)');
return b ? b.pop() : '';
}
let theme_list = ['{% static 'themes/bootstrap.min.css' %}', '{% static 'themes/flatly.min.css' %}', '{% static 'themes/superhero.min.css' %}'];
let css = $('#id_main_css');
let theme = getCookieValue('theme');
if (theme !== "" && theme_list.includes(theme)) {
css.attr('href', theme);
} else {
css.attr('href', '{% static 'themes/bootstrap.min.css' %}');
}
</script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
@ -161,15 +142,6 @@
<br/> <br/>
<br/> <br/>
<!-- Navbar theming, run before content to avoid flickering -->
<script type="text/javascript">
let nav_color = getCookieValue('color');
if (nav_color !== "") {
$('#id_main_nav').attr('class', 'navbar navbar-expand-lg navbar-dark bg-' + nav_color)
}
</script>
<div class="container"> <div class="container">
{% for message in messages %} {% for message in messages %}
@ -185,6 +157,8 @@
{% endblock %} {% endblock %}
</div> </div>
{% block script %}
{% endblock script %}
</body> </body>
</html> </html>

View File

@ -1,5 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %}
{% load static %} {% load static %}
{% block title %}{% trans 'Settings' %}{% endblock %} {% block title %}{% trans 'Settings' %}{% endblock %}
@ -40,57 +41,13 @@
<h4><i class="fas fa-palette"></i>{% trans 'Style' %}</h4> <h4><i class="fas fa-palette"></i>{% trans 'Style' %}</h4>
<div class="row"> <form action="." method="post">
<div class="col col-md-12"> {% csrf_token %}
<label> {{ form|crispy }}
{% trans 'Choose Theme' %} <button class="btn btn-success" type="submit"><i class="fas fa-save"></i> {% trans 'Save' %}</button>
<select class="form-control" id="id_select_theme" onchange="changeTheme()"> </form>
<option value="{% static 'themes/bootstrap.min.css' %}">{% trans 'Default' %}</option>
<option value="{% static 'themes/flatly.min.css' %}">Flatly</option>
<!--<option value="{% static 'themes/darkly.min.css' %}">Darkly</option>-->
<option value="{% static 'themes/superhero.min.css' %}">Superhero</option>
</select>
</label>
</div>
</div>
<div class="row">
<div class="col col-md-12">
<label>
{% trans 'Choose Navigation Color' %}
<select class="form-control" id="id_select_color" onchange="changeNavColor()">
<option value="primary">Primary</option>
<option value="secondary">Secondary</option>
<option value="info">Info</option>
<option value="success">Success</option>
<option value="warning">Warning</option>
<option value="danger">Danger</option>
</select>
</label>
</div>
</div>
<br/>
<br/>
<script type="text/javascript">
if (theme !== "") {
$('#id_select_theme').val(theme)
}
if (nav_color !== "") {
$('#id_select_color').val(nav_color)
}
function changeTheme() {
let theme = $('#id_select_theme').val();
document.cookie = "theme=" + theme + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
location.reload();
}
function changeNavColor() {
let color = $('#id_select_color').val();
document.cookie = "color=" + color + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
location.reload();
}
</script>
{% endblock %} {% endblock %}

View File

@ -2,6 +2,9 @@ from django import template
import markdown as md import markdown as md
import bleach import bleach
from bleach_whitelist import markdown_tags, markdown_attrs from bleach_whitelist import markdown_tags, markdown_attrs
from django.templatetags.static import static
from cookbook.models import UserPreference
register = template.Library() register = template.Library()
@ -14,3 +17,20 @@ def get_class(value):
@register.filter() @register.filter()
def markdown(value): def markdown(value):
return bleach.clean(md.markdown(value, extensions=['markdown.extensions.fenced_code']), markdown_tags, markdown_attrs) return bleach.clean(md.markdown(value, extensions=['markdown.extensions.fenced_code']), markdown_tags, markdown_attrs)
@register.simple_tag
def theme_url(request):
try:
themes = {
UserPreference.BOOTSTRAP: 'themes/bootstrap.min.css',
UserPreference.FLATLY: 'themes/flatly.min.css',
UserPreference.DARKLY: 'themes/darkly.min.css',
UserPreference.SUPERHERO: 'themes/superhero.min.css',
}
if request.user.userpreference.theme in themes:
return static(themes[request.user.userpreference.theme])
else:
raise AttributeError
except AttributeError:
return static('themes/bootstrap.min.css')

View File

@ -145,4 +145,19 @@ def shopping_list(request):
@login_required @login_required
def settings(request): def settings(request):
return render(request, 'settings.html', {}) up = request.user.userpreference
if request.method == "POST":
form = UserPreferenceForm(request.POST)
if form.is_valid():
if not up:
up = UserPreference(user=request.user)
up.theme = form.cleaned_data['theme']
up.save()
if up:
form = UserPreferenceForm(instance=up)
else:
form = UserPreferenceForm()
return render(request, 'settings.html', {'form': form})