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 (64)
Showing
with 220 additions and 89 deletions
......@@ -9,9 +9,35 @@ and this project adheres to `Semantic Versioning`_.
Unreleased
----------
Fixed
~~~~~
* The menu button used to be displayed twice on smaller screens.
* The icons were loaded from external servers instead from local server.
* Weekdays were not translated if system locales were missing
* Added locales-all to base image and note to docs
Changed
~~~~~~~
* [Dev] ActionForm now checks permissions on objects before executing
* [Dev] ActionForm now returns a proper return value from the executed action
2.8.1`_ - 2022-03-13
--------------------
Changed
~~~~~~~
* Official apps can now override any setting
`2.8`_ - 2022-03-11
-------------------
Added
~~~~~
* Add iconify icons
* Use identicons where avatars are missing.
* Display personal photos instead of avatars based on a site preference.
* Add an account menu in the top navbar.
......@@ -33,6 +59,11 @@ Fixed
* The ``reset password`` button on the login site used to overflow the card on smaller devices.
Deprecated
~~~~~~~~~~
* Legacy material icon font will be removed in AlekSIS-Core 3.0
`2.7.4`_ - 2022-02-09
---------------------
......@@ -777,3 +808,5 @@ Fixed
.. _2.7.2: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.7.2
.. _2.7.3: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.7.3
.. _2.7.4: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.7.4
.. _2.8: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.8
.. _2.8.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.8.1
......@@ -36,6 +36,7 @@ RUN apt-get -y update && \
less \
libpq-dev \
libssl-dev \
locales-all \
postgresql-client-14 \
pspg \
python3-dev \
......
......@@ -39,7 +39,7 @@ from .registries import (
site_preferences_registry,
)
from .util.auth_helpers import AppScopes
from .util.core_helpers import get_site_preferences
from .util.core_helpers import get_site_preferences, queryset_rules_filter
class PersonForm(ExtensibleForm):
......@@ -722,17 +722,37 @@ class ActionForm(forms.Form):
self.fields["selected_objects"].queryset = self.queryset
self.fields["action"].choices = self._get_action_choices()
def execute(self) -> bool:
def clean_action(self):
action = self._get_actions_dict().get(self.cleaned_data["action"], None)
if not action:
raise ValidationError(_("The selected action does not exist."))
return action
def clean_selected_objects(self):
action = self.cleaned_data["action"]
if hasattr(action, "permission"):
selected_objects = queryset_rules_filter(
self.request, self.cleaned_data["selected_objects"], action.permission
)
if selected_objects.count() < self.cleaned_data["selected_objects"].count():
raise ValidationError(
_("You do not have permission to run {} on all selected objects.").format(
getattr(value, "short_description", value.__name__)
)
)
return self.cleaned_data["selected_objects"]
def execute(self) -> Any:
"""Execute the selected action on all selected objects.
:return: If the form is not valid, it will return ``False``.
:return: the return value of the action
"""
if self.is_valid():
data = self.cleaned_data["selected_objects"]
action = self._get_actions_dict()[self.cleaned_data["action"]]
action(None, self.request, data)
return True
return False
action = self.cleaned_data["action"]
return action(None, self.request, data)
raise TypeError("execute() must be called on a pre-validated form.")
class ListActionForm(ActionForm):
......
......@@ -8,13 +8,13 @@ MENUS = {
{
"name": _("Login"),
"url": settings.LOGIN_URL,
"icon": "lock_open",
"svg_icon": "mdi:login-variant",
"validators": ["menu_generator.validators.is_anonymous"],
},
{
"name": _("Sign up"),
"url": "account_signup",
"icon": "how_to_reg",
"svg_icon": "mdi:account-plus-outline",
"validators": [
"menu_generator.validators.is_anonymous",
("aleksis.core.util.predicates.permission_validator", "core.can_register"),
......@@ -23,7 +23,7 @@ MENUS = {
{
"name": _("Accept invitation"),
"url": "enter_invitation_code",
"icon": "vpn_key",
"svg_icon": "mdi:key-outline",
"validators": [
"menu_generator.validators.is_anonymous",
("aleksis.core.util.predicates.permission_validator", "core.invite_enabled"),
......@@ -32,7 +32,7 @@ MENUS = {
{
"name": _("Dashboard"),
"url": "index",
"icon": "home",
"svg_icon": "mdi:home-outline",
"validators": [
("aleksis.core.util.predicates.permission_validator", "core.view_dashboard_rule")
],
......@@ -40,7 +40,7 @@ MENUS = {
{
"name": _("Notifications"),
"url": "notifications",
"icon": "notifications",
"svg_icon": "mdi:bell-outline",
"badge": unread_notifications_badge,
"validators": [
(
......@@ -49,10 +49,90 @@ MENUS = {
),
],
},
{
"name": _("Account"),
"url": "#",
"svg_icon": "mdi:account-outline",
"root": True,
"validators": ["menu_generator.validators.is_authenticated"],
"submenu": [
{
"name": _("Stop impersonation"),
"url": "impersonate-stop",
"svg_icon": "mdi:stop",
"validators": [
"menu_generator.validators.is_authenticated",
"aleksis.core.util.core_helpers.is_impersonate",
],
},
{
"name": _("Logout"),
"url": "logout",
"svg_icon": "mdi:logout-variant",
"validators": ["menu_generator.validators.is_authenticated"],
},
{
"name": _("2FA"),
"url": "two_factor:profile",
"svg_icon": "mdi:two-factor-authentication",
"validators": [
"menu_generator.validators.is_authenticated",
],
},
{
"name": _("Change password"),
"url": "account_change_password",
"svg_icon": "mdi:form-textbox-password",
"validators": [
"menu_generator.validators.is_authenticated",
(
"aleksis.core.util.predicates.permission_validator",
"core.can_change_password",
),
],
},
{
"name": _("Me"),
"url": "person",
"svg_icon": "mdi:emoticon-outline",
"validators": [
"menu_generator.validators.is_authenticated",
"aleksis.core.util.core_helpers.has_person",
],
},
{
"name": _("Preferences"),
"url": "preferences_person",
"svg_icon": "mdi:cog-outline",
"validators": [
"menu_generator.validators.is_authenticated",
"aleksis.core.util.core_helpers.has_person",
],
},
{
"name": _("Third-party accounts"),
"url": "socialaccount_connections",
"svg_icon": "mdi:earth",
"validators": [
"menu_generator.validators.is_authenticated",
"aleksis.core.util.core_helpers.has_person",
],
},
{
"name": _("Authorized applications"),
"url": "oauth2_provider:authorized-token-list",
"svg_icon": "mdi:gesture-tap-hold",
"validators": [
"menu_generator.validators.is_authenticated",
"aleksis.core.util.core_helpers.has_person",
],
},
],
},
{
"name": _("Admin"),
"url": "#",
"icon": "security",
"svg_icon": "mdi:security",
"validators": [
("aleksis.core.util.predicates.permission_validator", "core.view_admin_menu"),
],
......@@ -60,7 +140,7 @@ MENUS = {
{
"name": _("Announcements"),
"url": "announcements",
"icon": "announcement",
"svg_icon": "mdi:message-alert-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -71,7 +151,7 @@ MENUS = {
{
"name": _("School terms"),
"url": "school_terms",
"icon": "date_range",
"svg_icon": "mdi:calendar-range-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -82,7 +162,7 @@ MENUS = {
{
"name": _("Dashboard widgets"),
"url": "dashboard_widgets",
"icon": "dashboard",
"svg_icon": "mdi:view-dashboard-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -93,7 +173,7 @@ MENUS = {
{
"name": _("Data management"),
"url": "data_management",
"icon": "view_list",
"svg_icon": "mdi:chart-donut",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -104,7 +184,7 @@ MENUS = {
{
"name": _("System status"),
"url": "system_status",
"icon": "power_settings_new",
"svg_icon": "mdi:power-settings",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -115,7 +195,7 @@ MENUS = {
{
"name": _("Configuration"),
"url": "preferences_site",
"icon": "settings",
"svg_icon": "mdi:tune",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -126,13 +206,13 @@ MENUS = {
{
"name": _("Data checks"),
"url": "check_data",
"icon": "done_all",
"svg_icon": "mdi:list-status",
"validators": ["menu_generator.validators.is_superuser"],
},
{
"name": _("Manage permissions"),
"url": "manage_user_global_permissions",
"icon": "shield",
"svg_icon": "mdi:shield-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -143,7 +223,7 @@ MENUS = {
{
"name": _("Backend Admin"),
"url": "admin:index",
"icon": "settings",
"svg_icon": "mdi:database-cog-outline",
"validators": [
"menu_generator.validators.is_superuser",
],
......@@ -151,7 +231,7 @@ MENUS = {
{
"name": _("OAuth2 Applications"),
"url": "oauth2_applications",
"icon": "touch_app",
"svg_icon": "mdi:gesture-tap-hold",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -164,7 +244,7 @@ MENUS = {
{
"name": _("People"),
"url": "#",
"icon": "people",
"svg_icon": "mdi:account-group-outline",
"root": True,
"validators": [
("aleksis.core.util.predicates.permission_validator", "core.view_people_menu_rule")
......@@ -173,7 +253,7 @@ MENUS = {
{
"name": _("Persons"),
"url": "persons",
"icon": "person",
"svg_icon": "mdi:account-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -184,7 +264,7 @@ MENUS = {
{
"name": _("Groups"),
"url": "groups",
"icon": "group",
"svg_icon": "mdi:account-multiple-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -195,7 +275,7 @@ MENUS = {
{
"name": _("Group types"),
"url": "group_types",
"icon": "category",
"svg_icon": "mdi:shape-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -206,7 +286,7 @@ MENUS = {
{
"name": _("Groups and child groups"),
"url": "groups_child_groups",
"icon": "group_add",
"svg_icon": "mdi:account-multiple-plus-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -217,7 +297,7 @@ MENUS = {
{
"name": _("Additional fields"),
"url": "additional_fields",
"icon": "style",
"svg_icon": "mdi:palette-swatch-outline",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
......@@ -228,7 +308,7 @@ MENUS = {
{
"name": _("Invite person"),
"url": "invite_person",
"icon": "card_giftcard",
"svg_icon": "mdi:account-plus-outline",
"validators": [
"menu_generator.validators.is_authenticated",
("aleksis.core.util.predicates.permission_validator", "core.can_invite"),
......
......@@ -7,7 +7,12 @@ from django.utils.translation import gettext_lazy as _
from dynaconf import LazySettings
from .util.core_helpers import get_app_packages, merge_app_settings, monkey_patch
from .util.core_helpers import (
get_app_packages,
get_app_settings_overrides,
merge_app_settings,
monkey_patch,
)
monkey_patch()
......@@ -147,6 +152,7 @@ INSTALLED_APPS = [
"django_filters",
"oauth2_provider",
"rest_framework",
"dj_iconify.apps.DjIconifyConfig",
]
merge_app_settings("INSTALLED_APPS", INSTALLED_APPS, True)
......@@ -565,6 +571,8 @@ YARN_INSTALLED_APPS = [
"sortablejs",
"@sentry/tracing",
"luxon",
"@iconify/iconify",
"@iconify/json",
]
merge_app_settings("YARN_INSTALLED_APPS", YARN_INSTALLED_APPS, True)
......@@ -598,6 +606,7 @@ ANY_JS = {
"Sentry": {"js_url": JS_URL + "/@sentry/tracing/build/bundle.tracing.js"},
"cleavejs": {"js_url": JS_URL + "/cleave.js/dist/cleave.min.js"},
"luxon": {"js_url": JS_URL + "/luxon/build/global/luxon.min.js"},
"iconify": {"js_url": JS_URL + "/@iconify/iconify/dist/iconify.min.js"},
}
merge_app_settings("ANY_JS", ANY_JS, True)
......@@ -617,6 +626,8 @@ SASS_PROCESSOR_INCLUDE_DIRS = [
os.path.join(STATIC_ROOT, "public"),
]
ICONIFY_JSON_ROOT = os.path.join(JS_ROOT, "@iconify", "json")
ADMINS = _settings.get(
"contact.admins", [(AUTH_INITIAL_SUPERUSER["username"], AUTH_INITIAL_SUPERUSER["email"])]
)
......@@ -1001,3 +1012,5 @@ merge_app_settings("SHELL_PLUS_DONT_LOAD", SHELL_PLUS_DONT_LOAD)
# Add django-cleanup after all apps to ensure that it gets all signals as last app
INSTALLED_APPS.append("django_cleanup.apps.CleanupConfig")
locals().update(get_app_settings_overrides())
......@@ -192,6 +192,6 @@ $(document).ready(function () {
const channel = new BroadcastChannel("cache-or-not");
channel.addEventListener("message", event => {
if ((event.data) && !($("#cache-alert").length)) {
$("main").prepend('<div id="cache-alert" class="alert warning"><p><i class="material-icons left">warning</i>' + gettext("This page may contain outdated information since there is no internet connection.") + '</p> </div>')
$("main").prepend('<div id="cache-alert" class="alert warning"><p><i class="material-icons iconify left" data-icon="mdi:alert-outline"></i>' + gettext("This page may contain outdated information since there is no internet connection.") + '</p> </div>')
}
});
......@@ -9,11 +9,11 @@ const STYLE_CLASSES = {
};
const ICONS = {
10: 'info',
20: 'info',
25: 'check_circle',
30: 'warning',
40: 'error',
10: 'mdi:information',
20: 'mdi:information',
25: 'mdi:check-circle',
30: 'mdi:alert-outline',
40: 'mdi:alert-octagon-outline',
};
function setProgress(progress) {
......@@ -21,7 +21,7 @@ function setProgress(progress) {
}
function renderMessageBox(level, text) {
return '<div class="alert ' + STYLE_CLASSES[level] + '"><p><i class="material-icons left">' + ICONS[level] + '</i>' + text + '</p></div>';
return '<div class="alert ' + STYLE_CLASSES[level] + '"><p><i class="material-icons iconify left" data-icon="' + ICONS[level] + '"></i>' + text + '</p></div>';
}
function customProgress(progressBarElement, progressBarMessageElement, progress) {
......@@ -44,7 +44,7 @@ function customProgress(progressBarElement, progressBarMessageElement, progress)
function customSuccess(progressBarElement, progressBarMessageElement) {
setProgress(100);
$("#result-alert").addClass("success");
$("#result-icon").text("check_circle");
$("#result-icon").attr("data-icon", "mdi:check-circle-outline");
$("#result-text").text(OPTIONS.success);
$("#result-box").show();
$("#result-button").show();
......@@ -57,7 +57,7 @@ function customSuccess(progressBarElement, progressBarMessageElement) {
function customError(progressBarElement, progressBarMessageElement) {
setProgress(100);
$("#result-alert").addClass("error");
$("#result-icon").text("error");
$("#result-icon").attr("data-icon", "mdi:alert-octagon-outline");
$("#result-text").text(OPTIONS.error);
$("#result-box").show();
}
......
......@@ -429,41 +429,17 @@ th.orderable > a {
height: inherit;
}
th.orderable > a::after {
@extend .material-icons;
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'liga';
float: right;
content: "unfold_more";
th.orderable {
background: no-repeat right center;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12,18.17L8.83,15L7.42,16.41L12,21L16.59,16.41L15.17,15M12,5.83L15.17,9L16.58,7.59L12,3L7.41,7.59L8.83,9L12,5.83Z' /%3E%3C/svg%3E");
}
th.orderable.asc > a {
color: inherit;
&::after {
content: "expand_less";
}
th.orderable.asc {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z' /%3E%3C/svg%3E");
}
th.orderable.desc > a {
color: inherit;
&::after {
content: "expand_more";
}
th.orderable.desc {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z' /%3E%3C/svg%3E");
}
/*+++++++*/
......@@ -980,6 +956,14 @@ i.material-icons.new-notification {
object-fit: cover;
}
svg.iconify {
@extend i;
}
.btn .iconify.material-icons, .btn-flat .iconify.material-icons{
height: $button-height;
}
// Login Page
.login-card-action {
display: grid;
......
......@@ -6,7 +6,7 @@
<div class="container">
<div class="card red">
<div class="card-content white-text">
<i class="material-icons small left">error_outline</i>
<i class="material-icons iconify small left" data-icon="mdi:alert-octagon-outline"></i>
<span class="card-title">
{% if exception %}
{{ exception }}
......
......@@ -6,7 +6,7 @@
<div class="container">
<div class="card red">
<div class="card-content white-text">
<i class="material-icons small left">error_outline</i>
<i class="material-icons iconify small left" data-icon="mdi:alert-octagon-outline"></i>
<span class="card-title">{{ exception }}</span>
<p>
{% blocktrans %}
......
......@@ -6,7 +6,7 @@
<div class="container">
<div class="card red">
<div class="card-content white-text">
<i class="material-icons small left">error_outline</i>
<i class="material-icons iconify small left" data-icon="mdi:alert-octagon-outline"></i>
<span class="card-title">{% trans "Error" %} (500): {% blocktrans %}An unexpected error has
occured.{% endblocktrans %}</span>
<p>
......
......@@ -6,7 +6,7 @@
<div class="container">
<div class="card red">
<div class="card-content white-text">
<i class="material-icons small left">error_outline</i>
<i class="material-icons iconify small left" data-icon="mdi:alert-octagon-outline"></i>
<span class="card-title">{% blocktrans %}The maintenance mode is currently enabled. Please try again
later.{% endblocktrans %}</span>
<p>
......
......@@ -10,7 +10,7 @@
<div class="card red">
<div class="card-content white-text">
<div class="card-title">
<i class="material-icons small left">error_outline</i>
<i class="material-icons iconify small left" data-icon="mdi:alert-octagon-outline"></i>
{% blocktrans %}Account inactive.{% endblocktrans %}
</div>
<p>
......
......@@ -15,13 +15,13 @@
{% csrf_token %}
{% form form=form %}{% endform %}
{% trans "Confirm" as caption %}
{% include "core/partials/save_button.html" with caption=caption icon="how_to_reg" %}
{% include "core/partials/save_button.html" with caption=caption icon="mdi:account-plus-outline" %}
</form>
{% else %}
{% url "account_email" as email_url %}
<div class="alert warning">
<p>
<i class="material-icons left">warning</i>
<i class="material-icons iconify left" data-icon="mdi:alert-outline"></i>
{% blocktrans %}This e-mail confirmation link expired or is invalid. Please <a href="{{ email_url }}">issue a new e-mail confirmation request</a>.{% endblocktrans %}
</p>
</div>
......
......@@ -8,7 +8,7 @@
{% block content %}
<div class="alert warning">
<p>
<i class="material-icons left">warning</i>
<i class="material-icons iconify left" data-icon="mdi:alert-outline"></i>
{% trans "Forgot your current password? Click here to reset it:" %} <a href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>.
</p>
</div>
......@@ -17,7 +17,7 @@
{% csrf_token %}
{% form form=form %}{% endform %}
{% trans "Change password" as caption %}
{% include "core/partials/save_button.html" with caption=caption icon="priority_high" %}
{% include "core/partials/save_button.html" with caption=caption icon="mdi:exclamation" %}
</form>
{% endblock %}
......@@ -9,7 +9,7 @@
<div class="container">
<div class="card red">
<div class="card-content white-text">
<div class="material-icons small left">error_outline</div>
<div class="material-icons iconify small left" data-icon="mdi:alert-octagon-outline"></div>
<span class="card-title">{% blocktrans %}Changing of password disabled.{% endblocktrans %}</span>
<p>
{% blocktrans %}
......
......@@ -15,13 +15,13 @@
<div class="card-title">{% trans "Reset password" %}</div>
<p class="margin-bottom">
{% blocktrans %}Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it.{% endblocktrans %}
</p>
</p>
{% csrf_token %}
{% form form=form %}{% endform %}
</div>
</div>
<div class="card-action-light">
{% trans "Reset password" as caption %}
{% include "core/partials/save_button.html" with caption=caption icon="priority_high" %}
{% include "core/partials/save_button.html" with caption=caption icon="mdi:exclamation" %}
</div>
</form>
</div>
......
......@@ -11,7 +11,7 @@
<div class="card green">
<div class="card-content white-text">
<div class="card-title">
<i class="material-icons small left">check_circle</i>
<i class="material-icons iconify small left" data-icon="mdi:check-circle-outline"></i>
{% blocktrans %}Password reset mail sent{% endblocktrans %}
</div>
<p>
......
......@@ -11,7 +11,7 @@
<div class="card red">
<div class="card-content white-text">
<div class="card-title">
<i class="material-icons small left">error_outline</i>
<i class="material-icons iconify small left" data-icon="mdi:alert-octagon-outline"></i>
{% blocktrans %}Bad token{% endblocktrans %}
</div>
<p>
......@@ -44,7 +44,7 @@
{% form form=form %}{% endform %}
<div class="card-action-light">
{% trans "Change password" as caption %}
{% include "core/partials/save_button.html" with caption=caption icon="priority_high" %}
{% include "core/partials/save_button.html" with caption=caption icon="mdi:exclamation" %}
</div>
</div>
</form>
......@@ -52,7 +52,7 @@
</div>
<div class="alert success">
<p>
<i class="material-icons left">success</i>
<i class="material-icons iconify left" data-icon="mdi:check"></i>
{% blocktrans %}
Your password is now changed!
{% endblocktrans %}
......
......@@ -9,7 +9,7 @@
<div class="container">
<div class="card green">
<div class="card-content white-text">
<div class="material-icons small left">success</div>
<i class="material-icons iconify small left" data-icon="mdi:check"></i>
<span class="card-title">{% blocktrans %}Password changed!{% endblocktrans %}</span>
<p>
{% blocktrans %}
......