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 (165)
Showing
with 2592 additions and 1980 deletions
......@@ -65,6 +65,7 @@ docs/_build/
aleksis/node_modules/
aleksis/static/
aleksis/whoosh_index/
poetry.lock
.coverage
.mypy_cache/
......
include:
- project: "AlekSIS/official/AlekSIS"
file: /ci/general.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/prepare/lock.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/test.yml
- project: "AlekSIS/official/AlekSIS"
......
......@@ -9,6 +9,88 @@ and this project adheres to `Semantic Versioning`_.
Unreleased
----------
Added
~~~~~
* Add preference for configuring the default phone number country code.
Added
~~~~~
* OpenID Connect RSA keys can now be passed as string in config files
* Views filtering for person names now also search the username of a linked user
Fixed
~~~~~
* GroupManager.get_queryset() returned an incomplete QuerySet
* OAuth was broken by a non-semver-adhering django-oauth-toolkit update
* Too long texts in chips didn't result in a larger chip.
Changed
~~~~~~~
* Configuration files are now deep merged by default
* Improvements for shell_plus module loading
* core.Group model now takes precedence over auth.Group
* Name collisions are resolved by prefixing with the app label
* Apps can extend SHELL_PLUS_APP_PREFIXES and SHELL_PLUS_DONT_LOAD
* [Docker] Base image now contains curl, grep, less, sed, and pspg
* Views raising a 404 error can now customise the message that is displayed on the error page
* OpenID Connect is enabled by default now, without RSA support
`2.5`_ – 2022-01-02
-------------------
Added
~~~~~
* Recursive helper methods for group hierarchies
Fixed
~~~~~
* Remove left-over reference to preferences in a form definition that caused
form extensions in downstream apps to break
* Allow non-LDAP users to authenticate if LDAP is used with password handling
* Additional button on progress page for background tasks was shown even if the task failed.
* Register preference for available allowed oauth grants.
`2.4`_ – 2021-12-24
-------------------
Added
~~~~~
* Allow configuration of database options
* User invitations with invite codes and targeted invites for existing
persons
Fixed
~~~~~
* Correctly update theme colours on change again
* Use correct favicon as default AlekSIS favicon
* Show all years in a 200 year range around the current year in date pickers
* Imprint is now called "Imprint" and not "Impress".
* Logo files weren't uploaded to public namespace.
* Limit LDAP network timeouts to not hang indefinitely on login if LDAP
server is unreachable
Changed
~~~~~~~
* Modified the appearance of tables for mobile users to be more user friendly
* [Dev] Remove lock file; locking dependencies is the distribution's
responsibility
Removed
~~~~~~~
* Remove old generated AlekSIS icons
`2.3.1`_ – 2021-12-17
---------------------
......@@ -545,3 +627,5 @@ Fixed
.. _2.2.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.2.1
.. _2.3: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.3
.. _2.3.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.3.1
.. _2.4: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.4
.. _2.5: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.5
......@@ -19,6 +19,7 @@ ENV ALEKSIS_static__root /usr/share/aleksis/static
ENV ALEKSIS_media__root /var/lib/aleksis/media
ENV ALEKSIS_backup__location /var/lib/aleksis/backups
ENV ALEKSIS_dev__uwsgi__celery false
ENV PSQL_PAGER=pspg
# Install necessary Debian and PyPI packages for build and runtime
RUN apt-get -y update && \
......@@ -28,11 +29,15 @@ RUN apt-get -y update && \
eatmydata apt-get install -y --no-install-recommends \
build-essential \
chromium \
curl \
dumb-init \
gettext \
grep \
less \
libpq-dev \
libssl-dev \
postgresql-client-14 \
pspg \
python3-dev \
python3-magic \
python3-pip \
......
......@@ -6,7 +6,7 @@ This is the core of the AlekSIS framework and the official distribution
developers and administrators.
If you are looking for the AlekSIS standard distribution, i.e. the complete
software product ready for installation and usage, please visit the `AlekSIS`_
software product ready for installation and usage, please visit the `AlekSIS®`_
website or the distribution repository on `EduGit`_.
Features
......@@ -83,7 +83,7 @@ AlekSIS® is a registered trademark of the AlekSIS open source project, represen
by Teckids e.V. Please refer to the `trademark policy`_ for hints on using the trademark
AlekSIS®.
.. _AlekSIS: https://aleksis.org
.. _AlekSIS®: https://aleksis.org
.. _European Union Public Licence: https://eupl.eu/
.. _EduGit: https://edugit.org/AlekSIS/official/AlekSIS
.. _trademark policy: https://aleksis.org/pages/about
......@@ -54,6 +54,7 @@ class PersonFilter(FilterSet):
"additional_name__icontains",
"last_name__icontains",
"short_name__icontains",
"user__username__icontains",
],
label=_("Search by name"),
)
......
......@@ -6,7 +6,7 @@ from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
from django.core.exceptions import SuspiciousOperation, ValidationError
from django.db.models import QuerySet
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
......@@ -14,6 +14,7 @@ from django.utils.translation import gettext_lazy as _
from allauth.account.adapter import get_adapter
from allauth.account.forms import SignupForm
from allauth.account.utils import setup_user_email
from dj_cleavejs import CleaveWidget
from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget, Select2Widget
from dynamic_preferences.forms import PreferenceForm
from guardian.shortcuts import assign_perm
......@@ -393,6 +394,27 @@ DashboardWidgetOrderFormSet = forms.formset_factory(
)
class InvitationCodeForm(forms.Form):
"""Form to enter an invitation code."""
code = forms.CharField(
label=_("Invitation code"),
help_text=_("Please enter your invitation code."),
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Calculate number of fields
length = get_site_preferences()["auth__invite_code_length"]
packet_size = get_site_preferences()["auth__invite_code_packet_size"]
blocks = [
packet_size,
] * length
self.fields["code"].widget = CleaveWidget(blocks=blocks, delimiter="-", uppercase=True)
class SelectPermissionForm(forms.Form):
"""Select a permission to assign."""
......@@ -513,13 +535,42 @@ class AccountRegisterForm(SignupForm, ExtensibleForm):
"""Form to register new user accounts."""
class Meta:
model = Group
fields = []
model = Person
fields = [
"first_name",
"additional_name",
"last_name",
"street",
"housenumber",
"postal_code",
"place",
"date_of_birth",
"place_of_birth",
"sex",
"photo",
"mobile_number",
"phone_number",
"short_name",
"description",
]
layout = Layout(
Fieldset(
_("Base data"),
Row("first_name", "last_name"),
Row("first_name", "additional_name", "last_name"),
"short_name",
),
Fieldset(
_("Adress data"),
Row("street", "housenumber"),
Row("postal_code", "place"),
),
Fieldset(_("Contact data"), Row("mobile_number", "phone_number")),
Fieldset(
_("Additional data"),
Row("date_of_birth", "place_of_birth"),
Row("sex", "photo"),
"description",
),
Fieldset(
_("Account data"),
......@@ -527,72 +578,46 @@ class AccountRegisterForm(SignupForm, ExtensibleForm):
Row("email", "email2"),
Row("password1", "password2"),
),
Fieldset(
_("Consents"),
Row("privacy_policy"),
),
)
def __init__(self, *args, **kwargs):
super(AccountRegisterForm, self).__init__(*args, **kwargs)
self.fields["password1"] = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
privacy_policy = get_site_preferences()["footer__privacy_url"]
if settings.SIGNUP_PASSWORD_ENTER_TWICE:
self.fields["password2"] = forms.CharField(
label=_("Password (again)"), widget=forms.PasswordInput
)
self.fields["first_name"] = forms.CharField(
required=True,
)
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
self.fields["last_name"] = forms.CharField(
required=True,
)
if settings.SIGNUP_PASSWORD_ENTER_TWICE:
password2 = forms.CharField(label=_("Password (again)"), widget=forms.PasswordInput)
self.fields["privacy_policy"] = forms.BooleanField(
help_text=_(
f"I have read the <a href='{privacy_policy}'>Privacy policy</a>"
" and agree with them."
),
required=True,
)
def __init__(self, *args, **kwargs):
request = kwargs.pop("request", None)
super(AccountRegisterForm, self).__init__(*args, **kwargs)
def clean(self):
super(AccountRegisterForm, self).clean()
if request.session.get("account_verified_email"):
email = request.session["account_verified_email"]
dummy_user = get_user_model()
password = self.cleaned_data.get("password1")
if password:
try:
get_adapter().clean_password(password, user=dummy_user)
except forms.ValidationError as e:
self.add_error("password1", e)
person = Person.objects.get(email=email)
except (Person.DoesNotExist, Person.MultipleObjectsReturned):
raise SuspiciousOperation()
if (
settings.SIGNUP_PASSWORD_ENTER_TWICE
and "password1" in self.cleaned_data
and "password2" in self.cleaned_data
):
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
self.add_error(
"password2",
_("You must type the same password each time."),
)
return self.cleaned_data
self.fields["email"].disabled = True
self.fields["email2"].disabled = True
if person:
available_fields = [field.name for field in Person._meta.get_fields()]
for field in self.fields:
if field in available_fields and getattr(person, field):
self.fields[field].disabled = True
self.fields[field].initial = getattr(person, field)
def save(self, request):
adapter = get_adapter(request)
user = adapter.new_user(request)
adapter.save_user(request, user, self)
Person.objects.create(
first_name=self.cleaned_data["first_name"],
last_name=self.cleaned_data["last_name"],
email=self.cleaned_data["email"],
user=user,
)
# Create person
data = {}
for field in Person._meta.get_fields():
if field.name in self.cleaned_data:
data[field.name] = self.cleaned_data[field.name]
if not Person.objects.filter(email=data["email"]):
_person, created = Person.objects.update_or_create(user=user, **data)
self.custom_signup(request, user)
setup_user_email(request, user, [])
return user
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-13 12:55+0100\n"
"POT-Creation-Date: 2021-12-28 12:14+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -30,6 +30,6 @@ msgstr ""
msgid "OK"
msgstr ""
#: aleksis/core/static/js/main.js:127
#: aleksis/core/static/js/main.js:128
msgid "This page may contain outdated information since there is no internet connection."
msgstr ""
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-13 12:55+0100\n"
"POT-Creation-Date: 2021-12-28 12:14+0100\n"
"PO-Revision-Date: 2021-10-28 14:37+0000\n"
"Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
"Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis-core-js/de/>\n"
......@@ -30,6 +30,6 @@ msgstr "Abbrechen"
msgid "OK"
msgstr "OK"
#: aleksis/core/static/js/main.js:127
#: aleksis/core/static/js/main.js:128
msgid "This page may contain outdated information since there is no internet connection."
msgstr "Diese Seite enthält vielleicht veraltete Informationen, da es keine Internetverbindung gibt."
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-13 12:55+0100\n"
"POT-Creation-Date: 2021-12-28 12:14+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -30,6 +30,6 @@ msgstr ""
msgid "OK"
msgstr ""
#: aleksis/core/static/js/main.js:127
#: aleksis/core/static/js/main.js:128
msgid "This page may contain outdated information since there is no internet connection."
msgstr ""
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-13 12:55+0100\n"
"POT-Creation-Date: 2021-12-28 12:14+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -29,6 +29,6 @@ msgstr ""
msgid "OK"
msgstr ""
#: aleksis/core/static/js/main.js:127
#: aleksis/core/static/js/main.js:128
msgid "This page may contain outdated information since there is no internet connection."
msgstr ""
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-13 12:55+0100\n"
"POT-Creation-Date: 2021-12-28 12:14+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -29,6 +29,6 @@ msgstr ""
msgid "OK"
msgstr ""
#: aleksis/core/static/js/main.js:127
#: aleksis/core/static/js/main.js:128
msgid "This page may contain outdated information since there is no internet connection."
msgstr ""
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-13 12:55+0100\n"
"POT-Creation-Date: 2021-12-28 12:14+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -29,6 +29,6 @@ msgstr ""
msgid "OK"
msgstr ""
#: aleksis/core/static/js/main.js:127
#: aleksis/core/static/js/main.js:128
msgid "This page may contain outdated information since there is no internet connection."
msgstr ""
......@@ -7,6 +7,7 @@ from django.db.models import QuerySet
from django.db.models.manager import Manager
from calendarweek import CalendarWeek
from django_cte import CTEManager, CTEQuerySet
from polymorphic.managers import PolymorphicManager
......@@ -84,15 +85,15 @@ class SchoolTermRelatedQuerySet(QuerySet):
return None
class GroupManager(CurrentSiteManagerWithoutMigrations):
class GroupManager(CurrentSiteManagerWithoutMigrations, CTEManager):
"""Manager adding specific methods to groups."""
def get_queryset(self):
"""Ensure all related data is loaded as well."""
return super().get_queryset().select_related("school_term")
return Manager.get_queryset(self).select_related("school_term")
class GroupQuerySet(SchoolTermRelatedQuerySet):
class GroupQuerySet(SchoolTermRelatedQuerySet, CTEQuerySet):
pass
......