Skip to content
Snippets Groups Projects
Verified Commit 0e3cf477 authored by Tom Teichler's avatar Tom Teichler :beers:
Browse files

Merge branch 'master' into 75-migrate-to-django-3-0

parents 5dec1e01 49bc61aa
No related branches found
No related tags found
1 merge request!61Resolve "Migrate to Django 3.0"
Pipeline #285 failed
Showing
with 443 additions and 90 deletions
......@@ -56,3 +56,8 @@ docs/_build/
# Generated files
biscuit/static/
biscuit/node_modules/
.coverage
.tox/
maintenance_mode_state.txt
......@@ -9,7 +9,8 @@ New features
* Two-factor authentication with TOTP (Google Authenticator), Yubikey, SMS
and phone call.
* Devs: CRUDMixin provides a crud_event relation that returns all CRUD
events for an object
`1.0a2`_
--------
......
Contributor Covenant Code of Conduct
====================================
Our Pledge
----------
We as members, contributors, and leaders pledge to make participation in
our community a harassment-free experience for everyone, regardless of
age, body size, visible or invisible disability, ethnicity, sex
characteristics, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open,
welcoming, diverse, inclusive, and healthy community.
Our Standards
-------------
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our
mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political
attacks
- Public or private harassment
- Publishing others’ private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
Enforcement Responsibilities
----------------------------
Community leaders are responsible for clarifying and enforcing our
standards of acceptable behavior and will take appropriate and fair
corrective action in response to any behavior that they deem
inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct, and will
communicate reasons for moderation decisions when appropriate.
Scope
-----
This Code of Conduct applies within all community spaces, and also
applies when an individual is officially representing the community in
public spaces. Examples of representing our community include using an
official e-mail address, posting via an official social media account,
or acting as an appointed representative at an online or offline event.
Enforcement
-----------
Instances of abusive, harassing, or otherwise unacceptable behavior may
be reported to the community leaders responsible for enforcement at
foss@teckids.org. All complaints will be reviewed and investigated
promptly and fairly.
All community leaders are obligated to respect the privacy and security
of the reporter of any incident.
Enforcement Guidelines
----------------------
Community leaders will follow these Community Impact Guidelines in
determining the consequences for any action they deem in violation of
this Code of Conduct:
1. Correction
~~~~~~~~~~~~~
**Community Impact**: Use of inappropriate language or other behavior
deemed unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders,
providing clarity around the nature of the violation and an explanation
of why the behavior was inappropriate. A public apology may be
requested.
2. Warning
~~~~~~~~~~
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, for a specified period of
time. This includes avoiding interactions in community spaces as well as
external channels like social media. Violating these terms may lead to a
temporary or permanent ban.
3. Temporary Ban
~~~~~~~~~~~~~~~~
**Community Impact**: A serious violation of community standards,
including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No
public or private interaction with the people involved, including
unsolicited interaction with those enforcing the Code of Conduct, is
allowed during this period. Violating these terms may lead to a
permanent ban.
4. Permanent Ban
~~~~~~~~~~~~~~~~
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of
individuals.
**Consequence**: A permanent ban from any sort of public interaction
within the project community.
Attribution
-----------
This Code of Conduct is adapted from the `Contributor
Covenant <https://www.contributor-covenant.org>`__, version 2.0,
available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by `Mozilla’s code of conduct
enforcement ladder <https://github.com/mozilla/diversity>`__.
For answers to common questions about this code of conduct, see the FAQ
at https://www.contributor-covenant.org/faq. Translations are available
at https://www.contributor-covenant.org/translations.
Development principles and contribution guidelines
==================================================
In order to create a high-quality software product, the BiscuIT developers
have agreed upon fundamental principles governing the code layout, coding
style and repository management for BiscuIT and all official apps.
Coding layout and style
-----------------------
The coding style is defined in `PEP 8`_, with the following differences and
decisions:
- The maximum line length is 100 characters
- Imports are structured in five blocks, each of them sorted as defined in
PEP 8:
1. Standard library imports
2. Django imports
3. Third-party imports
4. Imports from other BiscuIT apps (absolute imports)
5. Imports from the same BiscuIT app (realtive imports)
- All string literals use single quotes
For the layout of source trees and style recommendations specific to Django,
the `Django coding style`_ is a good source of information, together with
the `Django Best Practices`_ collection.
Working with the Git repository
-------------------------------
The Git repository shall be used as a historic documentation of development
and as change management. It is important that the Git commit history
describes waht was changed, by whom and why.
Feature branches
~~~~~~~~~~~~~~~~
All features and bug fixes should be developed in their own branch and later
merged into the master branch as a whole. Of course, sometimes, it is
sensible to not do that, e.g. for fixing mere typos and the like
WIthin the feature branch, every logical step should be commited separately.
It is neither required nor desired to do micro-commits about every
development step. The commit history should describe the trains of thought
the design and implementation is based on.
Commit messages
~~~~~~~~~~~~~~~
Commit messages should be written as described in `How to Write a Git Commit
Message`_.
Commit messages should mention or even close any related issues. For merely
mentioning progress on an issue, use the keyword `advances`; for closing an
issue, use `closes`; for referring to a related issue for informational
purposes, use `cf.`. This should be done in the body of the commit message.
The subject of a commit message can (and should) be prepended with a tag in
square brackets if it relates to a certain part of the repository, e.g. [CI]
when changing CI/CD configuration or support code, [Dev] when changing
something in the development utilities, etc.
Manifestos governing development
--------------------------------
The FOSS community has created some manifestos describing several aspects of
software development, to agree upon a baseline for these aspects. The
BiscuIT developers have agreed to adhere to the following manifestos:
- The `Sane software manifesto`_
- The `Accessibility Manifesto`_
- The `User Data Manifesto`_
Not all theses from these manifestos are applicable. For example, most data
about persons in a school information system are dictated by the school and
probably governed by laws defining what and when to store. In that case,
giving the user control over these decisions is not possible. Developers
need to decide what should resonably be followed.
Text documents
--------------
If there is no objective reason against it, all text documents accompanying
the source use `reStructuredText`_.
Contributing to upstream
------------------------
If possible and reasonable, code that can be of use to others in the general
Django ecosystem shall be contributed to any upstream dependency, or a new
generalised upstream dependency be created, under the most permissive
licence possible.
.. _PEP 8: https://pep8.org/
.. _Django coding style: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/
.. _Django Best Practices: https://django-best-practices.readthedocs.io/en/latest/index.html
.. _How to Write a Git Commit Message: https://chris.beams.io/posts/git-commit/
.. _Sane software manifesto: https://sane-software.globalcode.info/
.. _Accessibility Manifesto: http://accessibilitymanifesto.com/
.. _User Data Manifesto: https://userdatamanifesto.org/
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
......@@ -21,11 +21,11 @@ RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
gettext \
libjs-bootstrap4 \
libpq5 \
libpq-dev \
libssl-dev \
netcat-openbsd
netcat-openbsd \
yarnpkg
# Install core dependnecies
WORKDIR /usr/src/app
......@@ -42,6 +42,7 @@ RUN mkdir -p /var/lib/biscuit/media /var/lib/biscuit/static /var/lib/biscuit/bac
# Build messages and assets
RUN python manage.py compilemessages; \
python manage.py yarn install; \
python manage.py collectstatic --no-input --clear
# Clean up build dependencies
......@@ -50,12 +51,14 @@ RUN apt-get remove --purge -y \
gettext \
libpq-dev \
libssl-dev \
python3-dev; \
yarnpkg; \
apt-get autoremove --purge -y; \
apt-get clean -y; \
pip uninstall -y poetry; \
rm -f /var/lib/apt/lists/*_*; \
rm -rf /root/.cache
rm -rf /root/.cache; \
rm -rf biscuit/node_modules; \
rm -rf /usr/local/lib/node_modules
# Declare a persistent volume for all data
VOLUME /var/lib/biscuit
......
include CODE_OF_CONDUCT.rst
include CONTRIBUTING.rst
include LICENCE
include manage.py
recursive-include biscuit/core/static *
recursive-include biscuit/core/templates *
recursive-include biscuit/core/migrations *
recursive-include docs *
Subproject commit a52358af2722329ae43916adee4b93208991ebc6
Subproject commit 9014661e518c6d9457723571d7b0ef1ac9be6c6b
Subproject commit c3dffaa6d1cd91af1f79103e71286e11daf4d62d
Subproject commit d4da734afa405a6af2adbe607f553e2b759737d6
Subproject commit f18823b68731884e9ed661ce92474a54708a6f72
Subproject commit b03369df8214f062a18a70824c6a257cf4ab427d
from django.contrib import admin
from .models import Group, Person, School, SchoolTerm
admin.site.register(Person)
admin.site.register(Group)
admin.site.register(School)
admin.site.register(SchoolTerm)
from glob import glob
import os
from warnings import warn
from django.apps import AppConfig, apps
from django.conf import settings
from django.db.utils import ProgrammingError
from django.db.models.signals import post_save
from .signals import clean_scss
class CoreConfig(AppConfig):
name = 'biscuit.core'
verbose_name = 'BiscuIT - The Free School Information System'
def clean_scss(self) -> None:
for source_map in glob(os.path.join(settings.STATIC_ROOT, '*.css.map')):
try:
os.unlink(source_map)
except OSError:
# Ignore because old is better than nothing
pass # noqa
def setup_data(self) -> None:
try:
apps.get_model('otp_yubikey', 'ValidationService').objects.update_or_create(
name='default', defaults={'use_ssl': True, 'param_sl': '', 'param_timeout': ''}
)
except ProgrammingError:
warn('Yubikey validation service could not be created yet. If you are currently in a migration, this is expected.')
def ready(self) -> None:
self.clean_scss()
self.setup_data()
clean_scss()
post_save.connect(clean_scss, sender=apps.get_model('dbsettings', 'Setting'))
# Generated by Django 2.2.5 on 2019-09-03 18:30
import biscuit.core.util.core_helpers
from django.conf import settings
from django.db import migrations, models
from django.utils.translation import ugettext_lazy as _
......@@ -70,7 +69,7 @@ class Migration(migrations.Migration):
('import_ref', models.CharField(blank=True, editable=False, max_length=64, null=True, verbose_name='Reference ID of import source')),
('guardians', models.ManyToManyField(related_name='children', to='core.Person', verbose_name='Guardians / Parents')),
('primary_group', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.Group')),
('school', models.ForeignKey(default=biscuit.core.util.core_helpers.get_current_school, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
('school', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='person', to=settings.AUTH_USER_MODEL)),
],
options={
......@@ -96,7 +95,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='group',
name='school',
field=models.ForeignKey(default=biscuit.core.util.core_helpers.get_current_school, on_delete=django.db.models.deletion.CASCADE, to='core.School'),
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.School'),
),
migrations.AlterUniqueTogether(
name='group',
......
# Generated by Django 2.2.5 on 2019-09-14 12:55
import biscuit.core.util.core_helpers
from django.db import migrations, models
import django.db.models.deletion
from django.utils.translation import ugettext_lazy as _
......@@ -32,7 +31,7 @@ class Migration(migrations.Migration):
('caption', models.CharField(max_length=30, verbose_name='Visible caption of the term')),
('date_start', models.DateField(null=True, verbose_name='Effective start date of term')),
('date_end', models.DateField(null=True, verbose_name='Effective end date of term')),
('school', models.ForeignKey(default=biscuit.core.util.core_helpers.get_current_school, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
('school', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
],
options={
'abstract': False,
......
from django.db import migrations
def create_validation_service(apps, schema_editor):
apps.get_model('otp_yubikey', 'ValidationService').objects.update_or_create(
name='default', defaults={'use_ssl': True, 'param_sl': '', 'param_timeout': ''}
)
class Migration(migrations.Migration):
dependencies = [
('core', '0003_school_logo'),
('otp_yubikey', '0001_initial'),
]
operations = [
migrations.RunPython(create_validation_service),
]
# Generated by Django 2.2.8 on 2019-12-09 08:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_yubi_otp'),
]
operations = [
migrations.RemoveField(
model_name='schoolterm',
name='school',
),
migrations.AlterField(
model_name='group',
name='name',
field=models.CharField(max_length=60, unique=True, verbose_name='Long name of group'),
),
migrations.AlterField(
model_name='group',
name='short_name',
field=models.CharField(max_length=16, unique=True, verbose_name='Short name of group'),
),
migrations.AlterField(
model_name='person',
name='import_ref',
field=models.CharField(blank=True, editable=False, max_length=64, null=True, unique=True, verbose_name='Reference ID of import source'),
),
migrations.AlterField(
model_name='person',
name='short_name',
field=models.CharField(blank=True, max_length=5, null=True, unique=True, verbose_name='Short name'),
),
migrations.AlterUniqueTogether(
name='group',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='person',
unique_together=set(),
),
migrations.RemoveField(
model_name='group',
name='school',
),
migrations.RemoveField(
model_name='person',
name='school',
),
]
......@@ -6,8 +6,6 @@ from django.db.models import QuerySet
from easyaudit.models import CRUDEvent
from .util.core_helpers import get_current_school
class ExtensibleModel(object):
""" Allow injection of code from BiscuIT apps to extend model functionality.
......@@ -69,16 +67,10 @@ class ExtensibleModel(object):
cls._safe_add(func, func.__name__)
class SchoolRelated(models.Model):
class CRUDMixin(models.Model):
class Meta:
abstract = True
# objects = SchoolRelatedManager()
school = models.ForeignKey(
'core.School', on_delete=models.CASCADE, default=get_current_school)
@property
def crud_events(self) -> QuerySet:
"""Get all CRUD events connected to this object from easyaudit."""
......
......@@ -4,10 +4,24 @@ from django.contrib.auth import get_user_model
from django.db import models
from django.utils.translation import ugettext_lazy as _
import dbsettings
from image_cropping import ImageCropField, ImageRatioField
from phonenumber_field.modelfields import PhoneNumberField
from .mixins import ExtensibleModel, SchoolRelated
from .mixins import ExtensibleModel
class ThemeSettings(dbsettings.Group):
colour_primary = dbsettings.StringValue(default='#007bff')
colour_secondary = dbsettings.StringValue(default='#6c757d')
colour_success = dbsettings.StringValue(default='#28a745')
colour_info = dbsettings.StringValue(default='#17a2b8')
colour_warning = dbsettings.StringValue(default='#ffc107')
colour_danger = dbsettings.StringValue(default='#dc3545')
colour_light = dbsettings.StringValue(default='#f8f9fa')
colour_dark = dbsettings.StringValue(default='#343a40')
theme_settings = ThemeSettings('Global theme settings')
class School(models.Model):
......@@ -30,7 +44,7 @@ class School(models.Model):
ordering = ['name', 'name_official']
class SchoolTerm(SchoolRelated):
class SchoolTerm(models.Model):
""" Information about a term (limited time frame) that data can
be linked to.
"""
......@@ -44,13 +58,12 @@ class SchoolTerm(SchoolRelated):
'Effective end date of term'), null=True)
class Person(SchoolRelated, ExtensibleModel):
class Person(models.Model, ExtensibleModel):
""" A model describing any person related to a school, including, but not
limited to, students, teachers and guardians (parents).
"""
class Meta:
unique_together = [['school', 'short_name'], ['school', 'import_ref']]
ordering = ['last_name', 'first_name']
SEX_CHOICES = [
......@@ -70,7 +83,7 @@ class Person(SchoolRelated, ExtensibleModel):
'Additional name(s)'), max_length=30, blank=True)
short_name = models.CharField(verbose_name=_(
'Short name'), max_length=5, blank=True, null=True)
'Short name'), max_length=5, blank=True, null=True, unique=True)
street = models.CharField(verbose_name=_(
'Street'), max_length=30, blank=True)
......@@ -96,7 +109,8 @@ class Person(SchoolRelated, ExtensibleModel):
photo_cropping = ImageRatioField('photo', '600x800', size_warning=True)
import_ref = models.CharField(verbose_name=_(
'Reference ID of import source'), max_length=64, blank=True, null=True, editable=False)
'Reference ID of import source'), max_length=64,
blank=True, null=True, editable=False, unique=True)
guardians = models.ManyToManyField('self', verbose_name=_('Guardians / Parents'),
symmetrical=False, related_name='children')
......@@ -132,19 +146,18 @@ class Person(SchoolRelated, ExtensibleModel):
return self.full_name
class Group(SchoolRelated, ExtensibleModel):
class Group(models.Model, ExtensibleModel):
"""Any kind of group of persons in a school, including, but not limited
classes, clubs, and the like.
"""
class Meta:
unique_together = [['school', 'name'], ['school', 'short_name']]
ordering = ['short_name', 'name']
name = models.CharField(verbose_name=_(
'Long name of group'), max_length=60)
'Long name of group'), max_length=60, unique=True)
short_name = models.CharField(verbose_name=_(
'Short name of group'), max_length=16)
'Short name of group'), max_length=16, unique=True)
members = models.ManyToManyField('Person', related_name='member_of')
owners = models.ManyToManyField('Person', related_name='owner_of')
......
......@@ -52,10 +52,12 @@ INSTALLED_APPS = [
'sass_processor',
'easyaudit',
'dbbackup',
'dbsettings',
'django_cron',
'bootstrap4',
'fa',
'django_any_js',
'django_yarnpkg',
'django_tables2',
'easy_thumbnails',
'image_cropping',
......@@ -80,16 +82,10 @@ INSTALLED_APPS += get_app_packages()
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'django_yarnpkg.finders.NodeModulesFinder',
'sass_processor.finders.CssFinder'
]
SASS_PROCESSOR_AUTO_INCLUDE = False
SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
'get-colour': 'biscuit.core.util.sass_helpers.get_colour',
}
SASS_PROCESSOR_INCLUDE_DIRS = [
_settings.get('bootstrap.sass_path', '/usr/share/sass/bootstrap')
]
MIDDLEWARE = [
# 'django.middleware.cache.UpdateCacheMiddleware',
......@@ -233,6 +229,7 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = _settings.get('static.url', '/static/')
MEDIA_URL = _settings.get('media.url', '/media/')
......@@ -241,41 +238,54 @@ LOGOUT_REDIRECT_URL = 'index'
STATIC_ROOT = _settings.get('static.root', os.path.join(BASE_DIR, 'static'))
MEDIA_ROOT = _settings.get('media.root', os.path.join(BASE_DIR, 'media'))
NODE_MODULES_ROOT = _settings.get('node_modules.root', os.path.join(BASE_DIR, 'node_modules'))
YARN_INSTALLED_APPS = [
'bootstrap',
'font-awesome',
'jquery',
'popper.js',
'datatables',
'select2'
]
FONT_AWESOME = {'url': _settings.get(
'bootstrap.fa_url', '/javascript/font-awesome/css/font-awesome.min.css')}
JS_URL = _settings.get('js_assets.url', STATIC_URL)
JS_ROOT = _settings.get('js_assets.root', NODE_MODULES_ROOT+'/node_modules')
FONT_AWESOME = {'url': JS_URL+'/font-awesome/css/font-awesome.min.css'}
BOOTSTRAP4 = {
'css_url': _settings.get('bootstrap.css_url', '/javascript/bootstrap4/css/bootstrap.min.css'),
'javascript_url': _settings.get('bootstrap.js_url', '/javascript/bootstrap4/js/bootstrap.min.js'),
'jquery_url': _settings.get('bootstrap.jquery_url', '/javascript/jquery/jquery.min.js'),
'popper_url': _settings.get('bootstrap.popper_url', '/javascript/popper.js/umd/popper.min.js'),
'css_url': JS_URL+'/bootstrap/dist//css/bootstrap.min.css',
'javascript_url': JS_URL+'/bootstrap/dist/js/bootstrap.min.js',
'jquery_url': JS_URL+'/jquery/dist/jquery.min.js',
'popper_url': JS_URL+'/popper.js/dist/umd/popper.min.js',
'include_jquery': True,
'include_popper': True,
'javascript_in_head': True
}
DATATABLES_BASE = _settings.get(
'bootstrap.datatables_base', '/javascript/jquery-datatables')
SELECT2_CSS = JS_URL+'/select2/dist/css/select2.min.css'
SELECT2_JS = JS_URL+'/select2/dist/js/select2.min.js'
SELECT2_I18N_PATH = JS_URL+'/select2/dist/js/i18n'
ANY_JS = {
'DataTables': {
'js_url': DATATABLES_BASE + '/jquery.dataTables.min.js'
'js_url': JS_URL+'/datatables/media/js/jquery.dataTables.min.js'
},
'DataTables-Bootstrap4': {
'css_url': DATATABLES_BASE + '/css/dataTables.bootstrap4.min.css',
'js_url': DATATABLES_BASE + '/dataTables.bootstrap4.min.js'
'css_url': JS_URL+'/datatables/media/css/dataTables.bootstrap4.min.css',
'js_url': JS_URL+'/datatables/media/js/dataTables.bootstrap4.min.js'
}
}
COLOUR_PRIMARY = _settings.get('theme.colours.primary', '#007bff')
COLOUR_SECONDARY = _settings.get('theme.colours.secondary', '#6c757d')
COLOUR_SUCCESS = _settings.get('theme.colours.success', '#28a745')
COLOUR_INFO = _settings.get('theme.colours.info', '#17a2b8')
COLOUR_WARNING = _settings.get('theme.colours.warning', '#ffc107')
COLOUR_DANGER = _settings.get('theme.colours.danger', '#dc3545')
COLOUR_LIGHT = _settings.get('theme.colours.light', '#f8f9fa')
COLOUR_DARK = _settings.get('theme.colours.dark', '#343a40')
SASS_PROCESSOR_AUTO_INCLUDE = False
SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
'get-colour': 'biscuit.core.util.sass_helpers.get_colour',
'get-theme-setting': 'biscuit.core.util.sass_helpers.get_theme_setting',
}
SASS_PROCESSOR_INCLUDE_DIRS = [
_settings.get('bootstrap.sass_path', JS_ROOT+'/bootstrap/scss/')
]
ADMINS = _settings.get('contact.admins', [])
SERVER_EMAIL = _settings.get('contact.from', 'root@localhost')
......
from glob import glob
import os
from django.conf import settings
def clean_scss(*args, **kwargs) -> None:
for source_map in glob(os.path.join(settings.STATIC_ROOT, '*.css.map')):
try:
os.unlink(source_map)
except OSError:
# Ignore because old is better than nothing
pass # noqa
$theme-colors: (
"primary": adjust-color(get-colour(get-setting(COLOUR_PRIMARY)), $alpha: 1),
"secondary": adjust-color(get-colour(get-setting(COLOUR_SECONDARY)), $alpha: 1),
"success": adjust-color(get-colour(get-setting(COLOUR_SUCCESS)), $alpha: 1),
"info": adjust-color(get-colour(get-setting(COLOUR_INFO)), $alpha: 1),
"warning": adjust-color(get-colour(get-setting(COLOUR_WARNING)), $alpha: 1),
"danger": adjust-color(get-colour(get-setting(COLOUR_DANGER)), $alpha: 1),
"light": adjust-color(get-colour(get-setting(COLOUR_LIGHT)), $alpha: 1),
"dark": adjust-color(get-colour(get-setting(COLOUR_DARK)), $alpha: 1),
"primary": adjust-color(get-colour(get-theme-setting(colour_primary)), $alpha: 1),
"secondary": adjust-color(get-colour(get-theme-setting(colour_secondary)), $alpha: 1),
"success": adjust-color(get-colour(get-theme-setting(colour_success)), $alpha: 1),
"info": adjust-color(get-colour(get-theme-setting(colour_info)), $alpha: 1),
"warning": adjust-color(get-colour(get-theme-setting(colour_warning)), $alpha: 1),
"danger": adjust-color(get-colour(get-theme-setting(colour_danger)), $alpha: 1),
"light": adjust-color(get-colour(get-theme-setting(colour_light)), $alpha: 1),
"dark": adjust-color(get-colour(get-theme-setting(colour_dark)), $alpha: 1),
);
@import "bootstrap";
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment