Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • hansegucker/AlekSIS-Core
  • pinguin/AlekSIS-Core
  • AlekSIS/official/AlekSIS-Core
  • sunweaver/AlekSIS-Core
  • sggua/AlekSIS-Core
  • edward/AlekSIS-Core
  • magicfelix/AlekSIS-Core
7 results
Show changes
Commits on Source (40)
Showing with 444 additions and 1425 deletions
......@@ -9,6 +9,39 @@ and this project adheres to `Semantic Versioning`_.
Unreleased
----------
Added
~~~~~
* Support config files in sub-directories
Fixed
~~~~~
* Use new MaterializeCSS fork because the old version is no longer maintained.
* Sender wasn't displayed for notifications on dashboard.
* Notifications and activities on dashboard weren't sorted from old to new.
`2.1.1`_ - 2021-11-14
---------------------
Added
~~~~~
* Provide ``SITE_PREFERENCES`` template variable for easier and request-independent access on all site preferences.
Fixed
~~~~~
* Make style.css and favicons cachable.
* Import model extensions from other apps before form extensions.
* Recreate backwards compatiblity for OAuth URLs by using ``oauth/`` again.
* Show correct logo and school title in print template if created in the background.
Removed
~~~~~~~
* Remove fallback code from optional Celery as it's now non-optional.
`2.1`_ - 2021-11-05
-------------------
......@@ -445,3 +478,4 @@ Fixed
.. _2.0rc7: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.0rc7
.. _2.0: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.0
.. _2.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.1
.. _2.1.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.1.1
from importlib import metadata
try:
from .celery import app as celery_app
except ModuleNotFoundError:
# Celery is not available
celery_app = None
from .celery import app as celery_app # noqa
try:
__version__ = metadata.distribution("AlekSIS-Core").version
......
......@@ -47,7 +47,7 @@ class CoreConfig(AppConfig):
from django.conf import settings # noqa
# Autodiscover various modules defined by AlekSIS
autodiscover_modules("form_extensions", "model_extensions", "checks")
autodiscover_modules("model_extensions", "form_extensions", "checks")
sitepreferencemodel = self.get_model("SitePreferenceModel")
personpreferencemodel = self.get_model("PersonPreferenceModel")
......@@ -102,7 +102,8 @@ class CoreConfig(AppConfig):
if new_value:
Favicon.on_site.update_or_create(
title=name, defaults={"isFavicon": is_favicon, "faviconImage": new_value},
title=name,
defaults={"isFavicon": is_favicon, "faviconImage": new_value},
)
else:
Favicon.on_site.filter(title=name, isFavicon=is_favicon).delete()
......
......@@ -180,7 +180,9 @@ class EditGroupForm(SchoolTermRelatedExtensibleForm):
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
"additional_fields": ModelSelect2MultipleWidget(
search_fields=["title__icontains",],
search_fields=[
"title__icontains",
],
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
}
......@@ -216,7 +218,10 @@ class AnnouncementForm(ExtensibleForm):
label=_("Groups"),
required=False,
widget=ModelSelect2MultipleWidget(
search_fields=["name__icontains", "short_name__icontains",],
search_fields=[
"name__icontains",
"short_name__icontains",
],
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
)
......@@ -363,7 +368,8 @@ class SchoolTermForm(ExtensibleForm):
class DashboardWidgetOrderForm(ExtensibleForm):
pk = forms.ModelChoiceField(
queryset=None, widget=forms.HiddenInput(attrs={"class": "pk-input"}),
queryset=None,
widget=forms.HiddenInput(attrs={"class": "pk-input"}),
)
order = forms.IntegerField(initial=0, widget=forms.HiddenInput(attrs={"class": "order-input"}))
......@@ -391,11 +397,20 @@ class AccountRegisterForm(SignupForm, ExtensibleForm):
fields = []
layout = Layout(
Fieldset(_("Base data"), Row("first_name", "last_name"),),
Fieldset(
_("Account data"), "username", Row("email", "email2"), Row("password1", "password2"),
_("Base data"),
Row("first_name", "last_name"),
),
Fieldset(
_("Account data"),
"username",
Row("email", "email2"),
Row("password1", "password2"),
),
Fieldset(
_("Consents"),
Row("privacy_policy"),
),
Fieldset(_("Consents"), Row("privacy_policy"),),
)
def __init__(self, *args, **kwargs):
......@@ -409,9 +424,13 @@ class AccountRegisterForm(SignupForm, ExtensibleForm):
label=_("Password (again)"), widget=forms.PasswordInput
)
self.fields["first_name"] = forms.CharField(required=True,)
self.fields["first_name"] = forms.CharField(
required=True,
)
self.fields["last_name"] = forms.CharField(required=True,)
self.fields["last_name"] = forms.CharField(
required=True,
)
self.fields["privacy_policy"] = forms.BooleanField(
help_text=_(
......@@ -439,7 +458,8 @@ class AccountRegisterForm(SignupForm, ExtensibleForm):
):
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
self.add_error(
"password2", _("You must type the same password each time."),
"password2",
_("You must type the same password each time."),
)
return self.cleaned_data
......
......@@ -34,7 +34,10 @@ MENUS = {
"icon": "notifications",
"badge": unread_notifications_badge,
"validators": [
("aleksis.core.util.predicates.permission_validator", "core.view_notifications",),
(
"aleksis.core.util.predicates.permission_validator",
"core.view_notifications",
),
],
},
{
......@@ -63,7 +66,9 @@ MENUS = {
"name": _("2FA"),
"url": "two_factor:profile",
"icon": "phonelink_lock",
"validators": ["menu_generator.validators.is_authenticated",],
"validators": [
"menu_generator.validators.is_authenticated",
],
},
{
"name": _("Change password"),
......@@ -210,7 +215,9 @@ MENUS = {
"name": _("Backend Admin"),
"url": "admin:index",
"icon": "settings",
"validators": ["menu_generator.validators.is_superuser",],
"validators": [
"menu_generator.validators.is_superuser",
],
},
{
"name": _("OAuth2 Applications"),
......
......@@ -352,7 +352,10 @@ class Person(ExtensibleModel):
send_templated_mail(
template_name="person_changed",
from_email=self.mail_sender_via,
headers={"Reply-To": self.mail_sender, "Sender": self.mail_sender,},
headers={
"Reply-To": self.mail_sender,
"Sender": self.mail_sender,
},
recipient_list=recipients,
context=context,
)
......@@ -467,7 +470,7 @@ class Group(SchoolTermRelatedExtensibleModel):
@property
def get_group_stats(self) -> dict:
""" Get stats about a given group """
"""Get stats about a given group"""
stats = {}
stats["members"] = len(self.members.all())
......@@ -687,7 +690,8 @@ class Announcement(ExtensibleModel):
verbose_name=_("Date and time from when to show"), default=timezone.now
)
valid_until = models.DateTimeField(
verbose_name=_("Date and time until when to show"), default=now_tomorrow,
verbose_name=_("Date and time until when to show"),
default=now_tomorrow,
)
@property
......
......@@ -21,6 +21,10 @@ for directory in DIRS_FOR_DYNACONF:
SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*.ini"))
SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*.yaml"))
SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*.toml"))
SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*/*.json"))
SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*/*.ini"))
SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*/*.yaml"))
SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*/*.toml"))
_settings = LazySettings(
ENVVAR_PREFIX_FOR_DYNACONF=ENVVAR_PREFIX_FOR_DYNACONF,
......@@ -227,7 +231,9 @@ if _settings.get("caching.redis.enabled", not IN_PYTEST):
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": _settings.get("caching.redis.address", REDIS_URL),
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",},
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
}
}
if REDIS_PASSWORD:
......@@ -253,10 +259,18 @@ SESSION_CACHE_ALIAS = "default"
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",},
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
AUTH_INITIAL_SUPERUSER = {
......@@ -387,7 +401,11 @@ if _settings.get("ldap.uri", None):
# Search attributes to find users by username
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(
*[
LDAPSearch(entry["base"], ldap.SCOPE_SUBTREE, entry.get("filter", "(uid=%(user)s)"),)
LDAPSearch(
entry["base"],
ldap.SCOPE_SUBTREE,
entry.get("filter", "(uid=%(user)s)"),
)
for entry in _AUTH_LDAP_USER_SETTINGS
]
)
......@@ -479,7 +497,7 @@ NODE_MODULES_ROOT = _settings.get("node_modules.root", os.path.join(BASE_DIR, "n
YARN_INSTALLED_APPS = [
"@fontsource/roboto",
"jquery",
"materialize-css",
"@materializecss/materialize",
"material-design-icons-iconfont",
"select2",
"select2-materialize",
......@@ -499,7 +517,7 @@ SELECT2_JS = JS_URL + "/select2/dist/js/select2.min.js"
SELECT2_I18N_PATH = JS_URL + "/select2/dist/js/i18n"
ANY_JS = {
"materialize": {"js_url": JS_URL + "/materialize-css/dist/js/materialize.min.js"},
"materialize": {"js_url": JS_URL + "/@materializecss/materialize/dist/js/materialize.min.js"},
"jQuery": {"js_url": JS_URL + "/jquery/dist/jquery.min.js"},
"material-design-icons": {
"css_url": JS_URL + "/material-design-icons-iconfont/dist/material-design-icons.css"
......@@ -529,7 +547,9 @@ SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
"get-preference": "aleksis.core.util.sass_helpers.get_preference",
}
SASS_PROCESSOR_INCLUDE_DIRS = [
_settings.get("materialize.sass_path", os.path.join(JS_ROOT, "materialize-css", "sass")),
_settings.get(
"materialize.sass_path", os.path.join(JS_ROOT, "@materializecss", "materialize", "sass")
),
os.path.join(STATIC_ROOT, "public"),
]
......@@ -725,7 +745,13 @@ CKEDITOR_CONFIGS = {
{"name": "colors", "items": ["TextColor", "BGColor"]},
{"name": "tools", "items": ["Maximize", "ShowBlocks"]},
{"name": "about", "items": ["About"]},
{"name": "customtools", "items": ["Preview", "Maximize",]},
{
"name": "customtools",
"items": [
"Preview",
"Maximize",
],
},
],
"toolbar": "Full",
"tabSpaces": 4,
......@@ -777,7 +803,10 @@ LOGGING = {
"null": {"class": "logging.NullHandler"},
},
"formatters": {"verbose": {"format": "%(levelname)s %(asctime)s %(module)s: %(message)s"}},
"root": {"handlers": ["console"], "level": _settings.get("logging.level", "WARNING"),},
"root": {
"handlers": ["console"],
"level": _settings.get("logging.level", "WARNING"),
},
"loggers": {},
}
......@@ -798,7 +827,10 @@ SILENCED_SYSTEM_CHECKS.append("guardian.W001")
AUTHENTICATION_BACKENDS.append("rules.permissions.ObjectPermissionBackend")
HAYSTACK_CONNECTIONS = {
"default": {"ENGINE": "haystack_redis.RedisEngine", "PATH": REDIS_URL,},
"default": {
"ENGINE": "haystack_redis.RedisEngine",
"PATH": REDIS_URL,
},
}
HAYSTACK_SIGNAL_PROCESSOR = "celery_haystack.signals.CelerySignalProcessor"
......
......@@ -11,7 +11,7 @@
{% block no_browser_title %}
{% block browser_title %}{% endblock %} —
{% endblock %}
{{ config.SITE_TITLE }}
{{ SITE_PREFERENCES.general__title }}
</title>
{% include_css "material-design-icons" %}
......@@ -51,7 +51,7 @@
<div id="print-header" class="row">
<div class="col s6 logo">
{% static "img/aleksis-banner.svg" as aleksis_banner %}
<img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}" alt="Logo"
<img src="{% firstof SITE_PREFERENCES.theme__logo.url aleksis_banner %}" alt="Logo"
id="print-logo"/>
</div>
<div class="col s6 right-align">
......@@ -65,7 +65,7 @@
<footer>
<div class="left">
{{ request.site.preferences.school__name }}
{{ SITE_PREFERENCES.school__name }}
</div>
<div class="right">
......
......@@ -85,7 +85,7 @@
<ul class="collection">
{% for notification in notifications %}
<li class="collection-item">
<span class="badge new primary-color">{{ notification.app }}</span>
<span class="badge new primary-color">{{ notification.sender }}</span>
<span class="title">{{ notification.title }}</span>
<p>
<i class="material-icons left">access_time</i> {{ notification.created }}
......@@ -108,7 +108,7 @@
</div>
{% endif %}
{% if user.person.preferences.general__automatically_update_dashboard and request.site.preferences.general__automatically_update_dashboard_site %}
{% if user.person.preferences.general__automatically_update_dashboard and SITE_PREFERENCES.general__automatically_update_dashboard_site %}
<script type="text/javascript" src="{% static "js/include_ajax_live.js" %}"></script>
{% endif %}
{% endblock %}
......@@ -3,20 +3,20 @@
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="description" content="{{ request.site.preferences.general__description }}"/>
<meta name="description" content="{{ SITE_PREFERENCES.general__description }}"/>
<meta name="generator" content="AlekSIS School Information System"/>
<meta name="theme-color" content="red">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="{{ request.site.preferences.general__title }}">
<meta name="apple-mobile-web-app-title" content="{{ SITE_PREFERENCES.general__title }}">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="msapplication-navbutton-color" content="{{ request.site.preferences.theme__primary }}">
<meta name="msapplication-TileColor" content="{{ request.site.preferences.theme__primary }}">
<meta name="msapplication-navbutton-color" content="{{ SITE_PREFERENCES.theme__primary }}">
<meta name="msapplication-TileColor" content="{{ SITE_PREFERENCES.theme__primary }}">
<meta name="msapplication-TileImage" content="{{ PWA_ICONS.microsoft.144.faviconImage.url }}">
<meta name="application-name" content="{{ request.site.preferences.general__title }}">
<meta name="application-name" content="{{ SITE_PREFERENCES.general__title }}">
<meta name="msapplication-starturl" content="/">
<meta name="msapplication-tap-highlight" content="no">
<meta name="browsermode" content="application">
......
......@@ -102,26 +102,26 @@ urlpatterns = [
ConnectDiscoveryInfoView.as_view(),
name="oidc_configuration",
),
path("oauth2/applications/", views.OAuth2ListView.as_view(), name="oauth2_applications"),
path("oauth/applications/", views.OAuth2ListView.as_view(), name="oauth2_applications"),
path(
"oauth2/applications/register/",
"oauth/applications/register/",
views.OAuth2RegisterView.as_view(),
name="register_oauth_application",
),
path(
"oauth2/applications/<int:pk>/", views.OAuth2DetailView.as_view(), name="oauth2_application"
"oauth/applications/<int:pk>/", views.OAuth2DetailView.as_view(), name="oauth2_application"
),
path(
"oauth2/applications/<int:pk>/delete/",
"oauth/applications/<int:pk>/delete/",
views.OAuth2DeleteView.as_view(),
name="delete_oauth2_application",
),
path(
"oauth2/applications/<int:pk>/edit/",
"oauth/applications/<int:pk>/edit/",
views.OAuth2EditView.as_view(),
name="edit_oauth2_application",
),
path("oauth2/", include("oauth2_provider.urls", namespace="oauth2_provider")),
path("oauth/", include("oauth2_provider.urls", namespace="oauth2_provider")),
path("__i18n__/", include("django.conf.urls.i18n")),
path(
"ckeditor/upload/",
......@@ -208,8 +208,16 @@ urlpatterns = [
),
path("health/", include(health_urls)),
path("health/pdf/", views.TestPDFGenerationView.as_view(), name="test_pdf"),
path("data_check/", views.DataCheckView.as_view(), name="check_data",),
path("data_check/run/", views.RunDataChecks.as_view(), name="data_check_run",),
path(
"data_check/",
views.DataCheckView.as_view(),
name="check_data",
),
path(
"data_check/run/",
views.RunDataChecks.as_view(),
name="data_check_run",
),
path(
"data_check/<int:pk>/<str:solve_option>/",
views.SolveDataCheckView.as_view(),
......
......@@ -260,5 +260,7 @@ class AppConfig(django.apps.AppConfig):
ct = ContentType.objects.get_for_model(model)
for perm, verbose_name in model.extra_permissions:
Permission.objects.get_or_create(
codename=perm, content_type=ct, defaults={"name": verbose_name},
codename=perm,
content_type=ct,
defaults={"name": verbose_name},
)
......@@ -213,6 +213,7 @@ def custom_information_processor(request: HttpRequest) -> dict:
"ADMINS": settings.ADMINS,
"PWA_ICONS": regrouped_pwa_icons,
"SENTRY_ENABLED": settings.SENTRY_ENABLED,
"SITE_PREFERENCES": get_site_preferences(),
}
if settings.SENTRY_ENABLED:
......
......@@ -184,9 +184,9 @@ def index(request: HttpRequest) -> HttpResponse:
person = DummyPerson()
widgets = []
activities = person.activities.all()[:5]
notifications = person.notifications.all()[:5]
unread_notifications = person.notifications.all().filter(read=False)
activities = person.activities.all().order_by("-created")[:5]
notifications = person.notifications.all().order_by("-created")[:5]
unread_notifications = person.notifications.all().filter(read=False).order_by("-created")
context["activities"] = activities
context["notifications"] = notifications
......
......@@ -29,9 +29,9 @@ copyright = "2019, 2020, AlekSIS team"
author = "AlekSIS team"
# The short X.Y version
version = "2.2"
version = "2.1"
# The full version, including alpha/beta/rc tags
release = "2.2.dev0"
release = "2.1.1"
# -- General configuration ---------------------------------------------------
......
This diff is collapsed.
......@@ -99,7 +99,7 @@ django-cachalot = "^2.3.2"
django-prometheus = "^2.1.0"
django-model-utils = "^4.0.0"
bs4 = "^0.0.1"
django-allauth = "^0.45.0"
django-allauth = "^0.46.0"
django-uwsgi-ng = "^1.1.0"
django-extensions = "^3.1.1"
ipython = "^7.20.0"
......@@ -110,7 +110,7 @@ boto3 = {version = "^1.17.33", optional = true}
django-cleanup = "^5.1.0"
djangorestframework = "^3.12.4"
Whoosh = "^2.7.4"
django-titofisto = "^0.1.0"
django-titofisto = "^0.2.0"
haystack-redis = "^0.0.1"
python-gnupg = "^0.4.7"
sentry-sdk = {version = "^1.4.3", optional = true}
......@@ -121,7 +121,7 @@ s3 = ["boto3", "django-storages"]
sentry = ["sentry"]
[tool.poetry.dev-dependencies]
aleksis-builddeps = "*"
aleksis-builddeps = "^5"
uwsgi = "^2.0"
[tool.poetry.scripts]
......