Merge pull request #970 from eeintech/color_themes

Color theme selection added to settings
This commit is contained in:
Oliver 2020-09-10 09:28:40 +10:00 committed by GitHub
commit 8c9491f3ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 3084 additions and 7 deletions

View File

@ -9,8 +9,9 @@ from django.utils.translation import ugettext as _
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field
from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText
from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText, StrictButton, Div
from django.contrib.auth.models import User
from common.models import ColorTheme
class HelperForm(forms.ModelForm):
@ -161,3 +162,36 @@ class SetPasswordForm(HelperForm):
'enter_password',
'confirm_password'
]
class ColorThemeSelectForm(forms.ModelForm):
""" Form for setting color theme """
name = forms.ChoiceField(choices=(), required=False)
class Meta:
model = ColorTheme
fields = [
'name'
]
def __init__(self, *args, **kwargs):
super(ColorThemeSelectForm, self).__init__(*args, **kwargs)
# Populate color themes choices
self.fields['name'].choices = ColorTheme.get_color_themes_choices()
self.helper = FormHelper()
# Form rendering
self.helper.form_show_labels = False
self.helper.layout = Layout(
Div(
Div(Field('name'),
css_class='col-sm-6',
style='width: 200px;'),
Div(StrictButton(_('Apply Theme'), css_class='btn btn-primary', type='submit'),
css_class='col-sm-6',
style='width: auto;'),
css_class='row',
),
)

View File

@ -82,6 +82,9 @@ STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'InvenTree', 'static'),
]
# Color Themes Directory
STATIC_COLOR_THEMES_DIR = os.path.join(STATIC_ROOT, 'css', 'color-themes')
# Web URL endpoint for served media files
MEDIA_URL = '/media/'

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
/* Color Theme: "Darker" by Radek Hladik */
.navbar-nav > li {
border-color: rgb(179, 179, 179);
}
.navbar-nav > li > a {
color:#0b2a62 !important;
}
.navbar-nav > li > a:hover {
color:#202020 !important;
}
.navbar-nav > .open > a {
color:#202020 !important;
}
.navbar {
background-color: rgb(189, 189, 189);
}
.table-condensed > tbody > tr > td {
border-top: 1px solid #062152 !important ;
}
.table-striped > tbody > tr > td {
border-top: 1px solid #92b3f1 ;
}
.table-bordered, .table-bordered > tbody > tr > td {
border: 1px solid rgb(182,182,182);
}
.table-bordered > thead > tr > th {
border: 1px solid rgb(182, 182, 182);
background-color: rgb(235, 235, 235);
}
h3 {
color:#06255d;
}

View File

@ -0,0 +1 @@
/* Color Theme: "Default" */

View File

@ -36,7 +36,7 @@ from django.views.generic.base import RedirectView
from rest_framework.documentation import include_docs_urls
from .views import IndexView, SearchView, DatabaseStatsView
from .views import SettingsView, EditUserView, SetPasswordView
from .views import SettingsView, EditUserView, SetPasswordView, ColorThemeSelectView
from .views import DynamicJsView
from .api import InfoView
@ -71,6 +71,7 @@ settings_urls = [
url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'),
url(r'^currency/?', SettingsView.as_view(template_name='InvenTree/settings/currency.html'), name='settings-currency'),
url(r'^part/?', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'),
url(r'^theme/?', ColorThemeSelectView.as_view(), name='settings-theme'),
url(r'^other/?', SettingsView.as_view(template_name='InvenTree/settings/other.html'), name='settings-other'),
# Catch any other urls

View File

@ -11,16 +11,17 @@ from __future__ import unicode_literals
from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
from django.http import JsonResponse, HttpResponseRedirect
from django.urls import reverse_lazy
from django.views import View
from django.views.generic import UpdateView, CreateView
from django.views.generic import UpdateView, CreateView, FormView
from django.views.generic.base import TemplateView
from part.models import Part, PartCategory
from stock.models import StockLocation, StockItem
from common.models import InvenTreeSetting
from common.models import InvenTreeSetting, ColorTheme
from .forms import DeleteForm, EditUserForm, SetPasswordForm
from .forms import DeleteForm, EditUserForm, SetPasswordForm, ColorThemeSelectForm
from .helpers import str2bool
from rest_framework import views
@ -556,6 +557,81 @@ class SettingsView(TemplateView):
return ctx
class ColorThemeSelectView(FormView):
""" View for selecting a color theme """
form_class = ColorThemeSelectForm
success_url = reverse_lazy('settings-theme')
template_name = "InvenTree/settings/theme.html"
def get_user_theme(self):
""" Get current user color theme """
try:
user_theme = ColorTheme.objects.filter(user=self.request.user).get()
except ColorTheme.DoesNotExist:
user_theme = None
return user_theme
def get_initial(self):
""" Select current user color theme as initial choice """
initial = super(ColorThemeSelectView, self).get_initial()
user_theme = self.get_user_theme()
if user_theme:
initial['name'] = user_theme.name
return initial
def get(self, request, *args, **kwargs):
""" Check if current color theme exists, else display alert box """
context = {}
form = self.get_form()
context['form'] = form
user_theme = self.get_user_theme()
if user_theme:
# Check color theme is a valid choice
if not ColorTheme.is_valid_choice(user_theme):
user_color_theme_name = user_theme.name
if not user_color_theme_name:
user_color_theme_name = 'default'
context['invalid_color_theme'] = user_color_theme_name
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
""" Save user color theme selection """
form = self.get_form()
# Get current user theme
user_theme = self.get_user_theme()
# Create theme entry if user did not select one yet
if not user_theme:
user_theme = ColorTheme()
user_theme.user = request.user
if form.is_valid():
theme_selected = form.cleaned_data['name']
# Set color theme to form selection
user_theme.name = theme_selected
user_theme.save()
return self.form_valid(form)
else:
# Set color theme to default
user_theme.name = ColorTheme.default_color_theme[0]
user_theme.save()
return self.form_invalid(form)
class DatabaseStatsView(AjaxView):
""" View for displaying database statistics """

View File

@ -0,0 +1,21 @@
# Generated by Django 3.0.7 on 2020-09-09 19:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0006_auto_20200203_0951'),
]
operations = [
migrations.CreateModel(
name='ColorTheme',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, default='', max_length=20)),
('user', models.CharField(max_length=150, unique=True)),
],
),
]

View File

@ -6,7 +6,10 @@ These models are 'generic' and do not fit a particular business logic object.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext as _
from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import ValidationError
@ -154,3 +157,49 @@ class Currency(models.Model):
self.value = 1.0
super().save(*args, **kwargs)
class ColorTheme(models.Model):
""" Color Theme Setting """
default_color_theme = ('', _('Default'))
name = models.CharField(max_length=20,
default='',
blank=True)
user = models.CharField(max_length=150,
unique=True)
@classmethod
def get_color_themes_choices(cls):
""" Get all color themes from static folder """
# Get files list from css/color-themes/ folder
files_list = []
for file in os.listdir(settings.STATIC_COLOR_THEMES_DIR):
files_list.append(os.path.splitext(file))
# Get color themes choices (CSS sheets)
choices = [(file_name.lower(), _(file_name.replace('-', ' ').title()))
for file_name, file_ext in files_list
if file_ext == '.css' and file_name.lower() != 'default']
# Add default option as empty option
choices.insert(0, cls.default_color_theme)
return choices
@classmethod
def is_valid_choice(cls, user_color_theme):
""" Check if color theme is valid choice """
try:
user_color_theme_name = user_color_theme.name
except AttributeError:
return False
for color_theme in cls.get_color_themes_choices():
if user_color_theme_name == color_theme[0]:
return True
return False

View File

@ -1,12 +1,13 @@
""" This module provides template tags for extra functionality
over and above the built-in Django tags.
"""
import os
from django import template
from InvenTree import version
from InvenTree import version, settings
from InvenTree.helpers import decimal2string
from common.models import InvenTreeSetting
from common.models import InvenTreeSetting, ColorTheme
register = template.Library()
@ -88,3 +89,22 @@ def inventree_docs_url(*args, **kwargs):
@register.simple_tag()
def inventree_setting(key, *args, **kwargs):
return InvenTreeSetting.get_setting(key)
@register.simple_tag()
def get_color_theme_css(username):
try:
user_theme = ColorTheme.objects.filter(user=username).get()
user_theme_name = user_theme.name
if not user_theme_name or not ColorTheme.is_valid_choice(user_theme):
user_theme_name = 'default'
except ColorTheme.DoesNotExist:
user_theme_name = 'default'
# Build path to CSS sheet
inventree_css_sheet = os.path.join('css', 'color-themes', user_theme_name + '.css')
# Build static URL
inventree_css_static_url = os.path.join(settings.STATIC_URL, inventree_css_sheet)
return inventree_css_static_url

View File

@ -8,6 +8,9 @@
<li{% ifequal tab 'part' %} class='active'{% endifequal %}>
<a href="{% url 'settings-part' %}"><span class='fas fa-shapes'></span> Part</a>
</li>
<li{% ifequal tab 'theme' %} class='active'{% endifequal %}>
<a href="{% url 'settings-theme' %}"><span class='fas fa-fill'></span> Theme</a>
</li>
{% if user.is_staff %}
<li{% ifequal tab 'other' %} class='active'{% endifequal %}>
<a href="{% url 'settings-other' %}"><span class='fas fa-cogs'></span> Other</a>

View File

@ -0,0 +1,32 @@
{% extends "InvenTree/settings/settings.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block tabs %}
{% include "InvenTree/settings/tabs.html" with tab='theme' %}
{% endblock %}
{% block settings %}
<div class='row'>
<div class='col-sm-6'>
<h4>Color Themes</h4>
</div>
</div>
<form action="{% url 'settings-theme' %}" method="post">
{% csrf_token %}
{% load crispy_forms_tags %}
{% crispy form %}
</form>
{% if invalid_color_theme %}
<div class="alert alert-danger alert-block" role="alert" style="display: inline-block;">
{% blocktrans %}
The CSS sheet "{{invalid_color_theme}}.css" for the currently selected color theme was not found.<br>
Please select another color theme :)
{% endblocktrans %}
</div>
{% endif %}
{% endblock %}

View File

@ -1,5 +1,6 @@
{% load static %}
{% load i18n %}
{% load inventree_extras %}
<!DOCTYPE html>
<html lang="en">
@ -39,6 +40,7 @@
<link rel="stylesheet" href="{% static 'css/select2-bootstrap.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-toggle.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% get_color_theme_css user.get_username %}">
{% block css %}
{% endblock %}