theming refactor
moved server side for a better page loading experience and less javascript mess
This commit is contained in:
@ -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)
|
||||||
|
24
cookbook/migrations/0013_userpreference.py
Normal file
24
cookbook/migrations/0013_userpreference.py
Normal 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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
26
cookbook/migrations/0014_auto_20200213_2332.py
Normal file
26
cookbook/migrations/0014_auto_20200213_2332.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
21
cookbook/migrations/0015_auto_20200213_2334.py
Normal file
21
cookbook/migrations/0015_auto_20200213_2334.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
25
cookbook/migrations/0016_auto_20200213_2335.py
Normal file
25
cookbook/migrations/0016_auto_20200213_2335.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
@ -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'
|
||||||
|
@ -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>
|
@ -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 %}
|
@ -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')
|
||||||
|
@ -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})
|
||||||
|
Reference in New Issue
Block a user