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 (7804)
module.exports = {
root: true,
overrides: [
{
files: ["*.js", "*.vue"],
// parser: "vue-eslint-parser",
//processor: "@graphql-eslint/graphql",
extends: [
"eslint:recommended",
"plugin:vue/strongly-recommended",
"plugin:@intlify/vue-i18n/recommended",
],
rules: {
"no-unused-vars": "warn",
"vue/no-unused-vars": "off",
"vue/multi-word-component-names": "off",
"vue/attribute-hyphenation": "error",
"vue/v-slot-style": "error",
"@intlify/vue-i18n/key-format-style": [
"error",
"snake_case",
{
splitByDots: false,
},
],
// "@intlify/vue-i18n/no-unused-keys": ["warn", {}],
"@intlify/vue-i18n/no-raw-text": [
"error",
{
ignoreNodes: ["v-icon"],
ignorePattern: "^[-–—·#:()\\[\\]&\\.\\s]+$",
},
],
"@intlify/vue-i18n/no-deprecated-tc": "off",
// Fixes for prettier (avoid eslint-config-prettier)
// The following rules can be used in some cases. See the README for more
// information. (These are marked with `0` instead of `"off"` so that a
// script can distinguish them.)
curly: 0,
"lines-around-comment": 0,
"max-len": 0,
"no-confusing-arrow": 0,
"no-mixed-operators": 0,
"no-tabs": 0,
"no-unexpected-multiline": 0,
quotes: 0,
"@typescript-eslint/quotes": 0,
"babel/quotes": 0,
"vue/html-self-closing": 0,
"vue/max-len": 0,
// The rest are rules that you never need to enable when using Prettier.
"array-bracket-newline": "off",
"array-bracket-spacing": "off",
"array-element-newline": "off",
"arrow-parens": "off",
"arrow-spacing": "off",
"block-spacing": "off",
"brace-style": "off",
"comma-dangle": "off",
"comma-spacing": "off",
"comma-style": "off",
"computed-property-spacing": "off",
"dot-location": "off",
"eol-last": "off",
"func-call-spacing": "off",
"function-call-argument-newline": "off",
"function-paren-newline": "off",
"generator-star": "off",
"generator-star-spacing": "off",
"implicit-arrow-linebreak": "off",
indent: "off",
"jsx-quotes": "off",
"key-spacing": "off",
"keyword-spacing": "off",
"linebreak-style": "off",
"multiline-ternary": "off",
"newline-per-chained-call": "off",
"new-parens": "off",
"no-arrow-condition": "off",
"no-comma-dangle": "off",
"no-extra-parens": "off",
"no-extra-semi": "off",
"no-floating-decimal": "off",
"no-mixed-spaces-and-tabs": "off",
"no-multi-spaces": "off",
"no-multiple-empty-lines": "off",
"no-reserved-keys": "off",
"no-space-before-semi": "off",
"no-trailing-spaces": "off",
"no-whitespace-before-property": "off",
"no-wrap-func": "off",
"nonblock-statement-body-position": "off",
"object-curly-newline": "off",
"object-curly-spacing": "off",
"object-property-newline": "off",
"one-var-declaration-per-line": "off",
"operator-linebreak": "off",
"padded-blocks": "off",
"quote-props": "off",
"rest-spread-spacing": "off",
semi: "off",
"semi-spacing": "off",
"semi-style": "off",
"space-after-function-name": "off",
"space-after-keywords": "off",
"space-before-blocks": "off",
"space-before-function-paren": "off",
"space-before-function-parentheses": "off",
"space-before-keywords": "off",
"space-in-brackets": "off",
"space-in-parens": "off",
"space-infix-ops": "off",
"space-return-throw-case": "off",
"space-unary-ops": "off",
"space-unary-word-ops": "off",
"switch-colon-spacing": "off",
"template-curly-spacing": "off",
"template-tag-spacing": "off",
"unicode-bom": "off",
"wrap-iife": "off",
"wrap-regex": "off",
"yield-star-spacing": "off",
"@babel/object-curly-spacing": "off",
"@babel/semi": "off",
"@typescript-eslint/brace-style": "off",
"@typescript-eslint/comma-dangle": "off",
"@typescript-eslint/comma-spacing": "off",
"@typescript-eslint/func-call-spacing": "off",
"@typescript-eslint/indent": "off",
"@typescript-eslint/keyword-spacing": "off",
"@typescript-eslint/member-delimiter-style": "off",
"@typescript-eslint/no-extra-parens": "off",
"@typescript-eslint/no-extra-semi": "off",
"@typescript-eslint/object-curly-spacing": "off",
"@typescript-eslint/semi": "off",
"@typescript-eslint/space-before-blocks": "off",
"@typescript-eslint/space-before-function-paren": "off",
"@typescript-eslint/space-infix-ops": "off",
"@typescript-eslint/type-annotation-spacing": "off",
"babel/object-curly-spacing": "off",
"babel/semi": "off",
"flowtype/boolean-style": "off",
"flowtype/delimiter-dangle": "off",
"flowtype/generic-spacing": "off",
"flowtype/object-type-curly-spacing": "off",
"flowtype/object-type-delimiter": "off",
"flowtype/quotes": "off",
"flowtype/semi": "off",
"flowtype/space-after-type-colon": "off",
"flowtype/space-before-generic-bracket": "off",
"flowtype/space-before-type-colon": "off",
"flowtype/union-intersection-spacing": "off",
"react/jsx-child-element-spacing": "off",
"react/jsx-closing-bracket-location": "off",
"react/jsx-closing-tag-location": "off",
"react/jsx-curly-newline": "off",
"react/jsx-curly-spacing": "off",
"react/jsx-equals-spacing": "off",
"react/jsx-first-prop-new-line": "off",
"react/jsx-indent": "off",
"react/jsx-indent-props": "off",
"react/jsx-max-props-per-line": "off",
"react/jsx-newline": "off",
"react/jsx-one-expression-per-line": "off",
"react/jsx-props-no-multi-spaces": "off",
"react/jsx-tag-spacing": "off",
"react/jsx-wrap-multilines": "off",
"standard/array-bracket-even-spacing": "off",
"standard/computed-property-even-spacing": "off",
"standard/object-curly-even-spacing": "off",
"unicorn/empty-brace-spaces": "off",
"unicorn/no-nested-ternary": "off",
"unicorn/number-literal-case": "off",
"vue/array-bracket-newline": "off",
"vue/array-bracket-spacing": "off",
"vue/arrow-spacing": "off",
"vue/block-spacing": "off",
"vue/block-tag-newline": "off",
"vue/brace-style": "off",
"vue/comma-dangle": "off",
"vue/comma-spacing": "off",
"vue/comma-style": "off",
"vue/dot-location": "off",
"vue/func-call-spacing": "off",
"vue/html-closing-bracket-newline": "off",
"vue/html-closing-bracket-spacing": "off",
"vue/html-end-tags": "off",
"vue/html-indent": "off",
"vue/html-quotes": "off",
"vue/key-spacing": "off",
"vue/keyword-spacing": "off",
"vue/max-attributes-per-line": "off",
"vue/multiline-html-element-content-newline": "off",
"vue/multiline-ternary": "off",
"vue/mustache-interpolation-spacing": "off",
"vue/no-extra-parens": "off",
"vue/no-multi-spaces": "off",
"vue/no-spaces-around-equal-signs-in-attribute": "off",
"vue/object-curly-newline": "off",
"vue/object-curly-spacing": "off",
"vue/object-property-newline": "off",
"vue/operator-linebreak": "off",
"vue/quote-props": "off",
"vue/script-indent": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/space-in-parens": "off",
"vue/space-infix-ops": "off",
"vue/space-unary-ops": "off",
"vue/template-curly-spacing": "off",
},
settings: {
"vue-i18n": {
localeDir: "./aleksis/core/frontend/messages/*.{json}",
messageSyntaxVersion: "^8.0.0",
},
},
env: {
es2021: true,
},
parserOptions: {
ecmaVersion: "latest",
},
},
{
files: ["*.graphql"],
parser: "@graphql-eslint/eslint-plugin",
plugins: ["@graphql-eslint"],
extends: "plugin:@graphql-eslint/operations-recommended",
parserOptions: {
graphQLConfig: {
schema: "./schema.json",
documents: "../aleksis/**/*/frontend/**/*.graphql",
},
},
rules: {
"@graphql-eslint/no-anonymous-operations": "error",
"@graphql-eslint/no-duplicate-fields": "error",
"@graphql-eslint/naming-convention": [
"error",
{
OperationDefinition: {
style: "camelCase",
forbiddenPrefixes: ["Query", "Mutation", "Subscription", "Get"],
forbiddenSuffixes: ["Query", "Mutation", "Subscription"],
},
},
],
},
},
],
};
{
"name": "aleksis-builddeps",
"version": "1.0.0",
"dependencies": {
"@graphql-eslint/eslint-plugin": "^4.3.0",
"@intlify/eslint-plugin-vue-i18n": "^3.0.0",
"eslint": "^8.26.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-vue": "^9.7.0",
"graphql": "^16.10.0",
"prettier": "^3.4.0",
"stylelint": "^16.0.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-standard": "^34.0.0"
}
}
......@@ -52,6 +52,11 @@ DEADJOE
.idea
.idea/
# VSCode
.vscode/
.history/
*.code-workspace
# Database
db.sqlite3
......@@ -62,13 +67,32 @@ docs/_build/
*.aux
# Generated files
aleksis/node_modules/
aleksis/static/
/cache
/node_modules
.dev-js/node_modules
/static/
/whoosh_index/
.vite
.dev-js/.yarn
.dev-js/.pnp.cjs
.dev-js/.pnp.loader.mjs
.dev-js/.yarnrc.yml
.dev-js/schema.json
# Lock files
poetry.lock
package-lock.json
yarn.lock
.dev-js/yarn.lock
# Tests
.coverage
.mypy_cache/
.tox/
htmlcov/
# Data
maintenance_mode_state.txt
media/
package-lock.json
aleksis/core/static/style.css
image: registry.edugit.org/teckids/docker-images/python-pimped:master
stages:
- test
- build
- deploy
variables:
GIT_SUBMODULE_STRATEGY: recursive
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
FF_NETWORK_PER_BUILD: "true"
cache:
key:
files:
- poetry.lock
- pyproject.toml
paths:
- .cache/pip
- .tox
test:
stage: test
services:
- name: selenium/standalone-firefox
alias: selenium
before_script:
- adduser --disabled-password --gecos "Test User" testuser
- chown -R testuser .
script:
- sudo -u testuser
env TEST_SELENIUM_HUB=http://selenium:4444/wd/hub
TEST_SELENIUM_BROWSERS=firefox
TEST_HOST=build
tox -e selenium -- --junitxml=.tox/junit.xml
artifacts:
paths:
- .tox/screenshots
reports:
junit: .tox/junit.xml
lint:
stage: test
script:
- tox -e lint,security
allow_failure: true
build_dist:
stage: build
script:
- tox -e build
artifacts:
paths:
- dist/
pages:
stage: deploy
before_script:
- cp -r .tox/screenshots/firefox docs/screenshots
script:
- tox -e docs -- BUILDDIR=../public/docs
artifacts:
paths:
- public/
only:
- master
build_docker:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" >/kaniko/.docker/config.json
- /kaniko/executor
--context $CI_PROJECT_DIR
--dockerfile $CI_PROJECT_DIR/Dockerfile
--destination $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
--cache=true
--cleanup
- /kaniko/executor
--context $CI_PROJECT_DIR/docker/nginx
--dockerfile $CI_PROJECT_DIR/docker/nginx/Dockerfile
--destination $CI_REGISTRY_IMAGE/nginx:$CI_COMMIT_REF_NAME
--cache=true
--cleanup
only:
- master
- tags
deploy_demo-master:
stage: deploy
environment:
name: demo/master
url: http://demo-master.aleksis.org
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- grep -v "build:" docker-compose.yml | ssh root@demo-master.aleksis.org
env ALEKSIS_IMAGE_TAG=${CI_COMMIT_REF_NAME}
docker-compose
-p aleksis-${CI_ENVIRONMENT_SLUG}
-f /dev/stdin
pull
- grep -v "build:" docker-compose.yml | ssh root@demo-master.aleksis.org
env ALEKSIS_IMAGE_TAG=${CI_COMMIT_REF_NAME}
NGINX_HTTP_PORT=80
ALEKSIS_maintenance__debug=true
docker-compose
-p aleksis-${CI_ENVIRONMENT_SLUG}
-f /dev/stdin
up -d
only:
- master
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/lint.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/security.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/test.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/build/dist.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/publish/pypi.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/docker/image.yml
- project: "AlekSIS/official/AlekSIS"
file: "/ci/deploy/review.yml"
- project: "AlekSIS/official/AlekSIS"
file: "/ci/deploy/trigger_dist.yml"
[submodule "apps/official/AlekSIS-App-Alsijil"]
path = apps/official/AlekSIS-App-Alsijil
url = https://edugit.org/AlekSIS/AlekSIS-App-Alsijil
ignore = untracked
[submodule "apps/official/AlekSIS-App-Chronos"]
path = apps/official/AlekSIS-App-Chronos
url = https://edugit.org/AlekSIS/AlekSIS-App-Chronos
ignore = untracked
[submodule "apps/official/AlekSIS-App-Exlibris"]
path = apps/official/AlekSIS-App-Exlibris
url = https://edugit.org/AlekSIS/AlekSIS-App-Exlibris
ignore = untracked
......@@ -8,7 +8,9 @@ Jonathan Weth <git@jonathanweth.de> Jonathan Weth <joniweth@gmx.de>
Jonathan Weth <git@jonathanweth.de> Jonathan Weth <mail@jonathanweth.de>
Jonathan Weth <git@jonathanweth.de> Jonathan Weth <wethjo@katharineum.de>
Julian Leucker <leuckerj@gmail.com> Julian <leuckerj@gmail.com>
Lloyd Meins <git@lloydmeins.de> Aithus <lloydmeins@gmx.net>
Silas Della Contrada <s.developer@4-dc.de> sdcieo0330 <silasdc0@gmail.com>
Tom Teichler <tom.teichler@teckids.org> Tom Teichler <t.teichler@babiel.com>
mirabilos <thorsten.glaser@teckids.org> mirabilos <mirabilos@evolvis.org>
mirabilos <thorsten.glaser@teckids.org> mirabilos <t.glaser@tarent.de>
root (Skolelinux) <root@tjener.intern> root <root@tjener.intern>
......
# Byte-compiled / optimized / DLL files
*$py.class
*.py[cod]
__pycache__/
# Distribution / packaging
*.egg
*.egg-info/
.Python
.eggs/
.installed.cfg
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
# Installer logs
pip-delete-this-directory.txt
pip-log.txt
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# pyenv
.python-version
# Environments
.env
.venv
ENV/
env/
venv/
# Editors
*~
DEADJOE
\#*#
# IntelliJ
.idea
.idea/
# Database
db.sqlite3
# Sphinx
docs/_build/
# TeX
*.aux
# Generated files
/node_modules/
/static/
/whoosh_index/
poetry.lock
.coverage
.mypy_cache/
.tox/
htmlcov/
maintenance_mode_state.txt
media/
package-lock.json
yarn.lock
# VSCode
.vscode/
.history/
*.code-workspace
/cache
# Add HTML files to avoid problems with unsupported Django templates
*.html
# Do not check/reformat generated files
aleksis/core/util/licenses.json
.vite/
.pnp.cjs
.pnp.loader.mjs
.git/
{
"extends": ["stylelint-config-standard", "stylelint-config-prettier"]
}
This diff is collapsed.
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 AlekSIS developers
have agreed upon fundamental principles governing the code layout, coding
style and repository management for AlekSIS and all official apps.
Coding layout and style
-----------------------
The coding style is defined in `PEP 8`_, with the following differences and
decisions:
- The defaults of the `black`_ code formatter are used
- This implies all string literals usin double-quotes, if it does not lead
to more escaping. As proposed by `black`: "My recommendation here is to
keep using whatever is faster to type and let Black handle the transformation."
- The maximum line length is 100 characters
- Imports are structured in five blocks, each of them sorted as defined in
PEP 8 and the Django style guide:
1. Standard library imports
2. Django imports
3. Third-party imports
4. Imports from AlekSIS core and other apps (absolute imports)
5. Imports from the same AlekSIS app (realtive imports)
Use `isort` to take care of this
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.
To ensure code is styled correctly, before commiting, run::
tox -e reformat
Text documents
~~~~~~~~~~~~~~
If there is no objective reason against it, all text documents accompanying
the source use `reStructuredText`_.
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.
Help and information on Git for beginners are available in the `Git guide`_
Feature and issue 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.
If you work on multiple issues at the same time, you have to change between
branches. Never work on unrelated issues in the same branch.
Branches should either contain the number and title of the related issue (as
generated by GitLab), or follow the naming convention type/name, where type
is one of bugfix, feature, or refactor.
All changes on the code should be commited and pushed before stopping work on
in order to prevent data loss. If a logical step is continued later, you
should amend and force-push the commit.
Issue branches should be rebased onto the current master regularly to avoid
merge conflicts.
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.
Example::
Solve LDAP connection problems
- Add the ldap-with-unicorn-dust dependency
- Configure settings.py to accept the correct groups from LDAP
Closes #10.
Merge Requests
~~~~~~~~~~~~~~
If you think that the work on your feature branch is finished, you have to
create a merge request on EduGit in order to let other developers and the
maintainers take a look at it.
See below on how to submit patches if you cannot use the development
platform.
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
AlekSIS 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.
The case on supporting non-free services
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Defined by the `Free Software Definition`_, it is an essential freedom to
be allowed to use free software for any purpose, without limitation. Thus,
interoperability with non-free services shall not be ruled out, and the
AlekSIS project explicitly welcomes implementing support for
interoperability with non-free services.
However, to purposefully foster free software and services, if
interoperability for a certain kind of non-free service is implemented, this
must be done in a generalised manner (i.e. using open protocols and
interfaces). For example, if implementing interoperability with some
cloud-hosted calendar provider can be implemented either through a
proprietary API, or through a standard iCalendar/Webcal interfaces, the
latter is to be preferred. Lacking such support, if a proprietary service
is connected through a proprietary, single-purpose interface, measures shall
be taken to also support alternative free services.
Documentation
-------------
The documentation in the AlekSIS project shall consist of three layers.
Source code comments
~~~~~~~~~~~~~~~~~~~~
The parts of your code that are not self-explaining have to be commented.
Ideally, source code is self-explaining, in the sense that its logical
structure, naming of variables, and the like makes it easy to read and
understand, for a reasonably talented programmer, to follow what it does.
Docstrings
~~~~~~~~~~
All functions, methods, classes and modules that are newly added (or changed
extensively) must contain a docstring for other developers to understand
what it does. Docstrings of public elements will be included in the
developer documentation.
Sphinx documentation
~~~~~~~~~~~~~~~~~~~~
In addition to that you should document the function or the way the app
works in the project documentation (`docs/` directory). Use that especially
for functionality which is shared by your app for other apps (public APIs).
Your Sphinx documentation should contain what the API can and shall be sued
for, and how other apps can benefit from it.
When creating a new app, also include documentation about it targeted at
administrators and users. At least you have to document what new developers
and users have to do in order to get a working instance of the app.
Sphinx documentation for all official apps will be published together.
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.
How to contact the team
-----------------------
Development platform
~~~~~~~~~~~~~~~~~~~~
Main development of AlekSIS is done on the `EduGit`_ platform in the
`AlekSIS group`_ and discussions are held on the linked `Mattermost team`_.
All platforms and tools mandated for development are free software and
freely usable. EduGit accepts a variety of sources for login, so
contributors are free to decide where they want to register in order to
participate.
If any contributor cannot use the platforms for whatever reasons, patches and
questions directed at the developers can also be e-mailed to
<aleksis-dev@lists.teckids.org>.
.. _PEP 8: https://pep8.org/
.. _Django coding style: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/
.. _black: https://black.readthedocs.io/en/stable/
.. _Django Best Practices: https://django-best-practices.readthedocs.io/en/latest/index.html
.. _Git guide: https://rogerdudler.github.io/git-guide/
.. _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/
.. _Free Software Definition: https://www.gnu.org/philosophy/free-sw.en.html
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _EduGit: https://edugit.org/
.. _AlekSIS group: https://edugit.org/AlekSIS/
.. _Mattermost team: https://mattermost.edugit.org/biscuit/
FROM python:3.8-buster
FROM debian:bookworm-slim AS core
# Build arguments
ARG EXTRAS="ldap,s3,sentry"
ARG APP_VERSION="==3.0b0"
ARG PIP_EXTRA_INDEX_URL="https://edugit.org/api/v4/projects/461/packages/pypi/simple"
# Configure Python to be nice inside Docker and pip to stfu
ENV PYTHONUNBUFFERED 1
......@@ -6,59 +11,130 @@ ENV PYTHONDONTWRITEBYTECODE 1
ENV PIP_DEFAULT_TIMEOUT 100
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
ENV PIP_NO_CACHE_DIR 1
ENV PIP_EXTRA_INDEX_URL $PIP_EXTRA_INDEX_URL
ENV PIP_USE_DEPRECATED legacy-resolver
ENV DEBIAN_FRONTEND noninteractive
# Configure app settings for build and runtime
ENV ALEKSIS_static__root /var/lib/aleksis/static
ENV ALEKSIS_caching__dir /var/cache/aleksis
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 packages for build and runtime
# Install necessary Debian and PyPI packages for build and runtime
RUN apt-get -y update && \
apt-get -y install eatmydata && \
apt-get -y install eatmydata gnupg postgresql-common && \
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
eatmydata apt-get -y upgrade && \
eatmydata apt-get install -y --no-install-recommends \
build-essential \
curl \
dumb-init \
gettext \
libpq5 \
grep \
less \
libpq-dev \
libssl-dev \
netcat-openbsd \
yarnpkg
locales-all \
postgresql-client-14 \
postgresql-client-15 \
postgresql-client-16 \
pspg \
python3-dev \
python3-magic \
python3-pip \
uwsgi \
uwsgi-plugin-python3 \
yarnpkg \
git
# Install core dependnecies
WORKDIR /usr/src/app
COPY poetry.lock pyproject.toml ./
RUN eatmydata pip install poetry; \
poetry export -f requirements.txt | eatmydata pip install -r /dev/stdin; \
eatmydata pip install gunicorn
# Install extra dependencies
RUN case ",$EXTRAS," in \
(*",ldap,"*) \
eatmydata apt-get install -y --no-install-recommends \
libldap2-dev \
libsasl2-dev \
ldap-utils \
;; \
esac
# Install core
COPY aleksis ./aleksis/
COPY LICENCE.rst README.rst manage.py ./
RUN mkdir -p /var/lib/aleksis/media /var/lib/aleksis/static /var/lib/aleksis/backups; \
poetry build && eatmydata pip install dist/*.whl
RUN set -e; \
mkdir -p ${ALEKSIS_caching__dir} \
${ALEKSIS_static__root} \
${ALEKSIS_media__root} \
${ALEKSIS_backup__location}; \
dpkg-divert --rename --add /usr/lib/$(py3versions -d)/EXTERNALLY-MANAGED; \
eatmydata pip install AlekSIS-Core\[$EXTRAS\]$APP_VERSION
# Define entrypoint, volumes and uWSGI running on port 8000
EXPOSE 8000
COPY docker-startup.sh /usr/local/bin/aleksis-docker-startup
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/usr/local/bin/aleksis-docker-startup"]
# Build messages and assets
RUN python manage.py compilemessages; \
eatmydata python manage.py yarn install
# Install assets
FROM core as assets
RUN eatmydata aleksis-admin vite build; \
eatmydata aleksis-admin compile_scss; \
eatmydata aleksis-admin collectstatic --no-input; \
rm -rf /usr/local/share/.cache
# FIXME Introduce deletion after we don't need materializecss anymore for SASS
# also in ONBUILD below
# rm -rf /usr/local/share/.cache ${ALEKSIS_caching__dir}/*
# Clean up build dependencies
RUN eatmydata apt-get remove --purge -y \
FROM assets AS clean
RUN set -e; \
eatmydata apt-get remove --purge -y \
build-essential \
gettext \
gnupg \
libpq-dev \
libssl-dev \
yarnpkg; \
libldap2-dev \
libsasl2-dev \
python3-dev; \
eatmydata apt-get autoremove --purge -y; \
apt-get clean -y; \
eatmydata pip uninstall -y poetry; \
rm -f /var/lib/apt/lists/*_*; \
rm -rf /root/.cache
# Declare a persistent volume for all data
VOLUME /var/lib/aleksis
# Drop privileges for runtime to www-data
FROM clean AS unprivileged
WORKDIR /var/lib/aleksis
RUN chown -R www-data:www-data \
${ALEKSIS_media__root} \
${ALEKSIS_backup__location}
USER 33:33
VOLUME ${ALEKSIS_media__root} ${ALEKSIS_backup__location}
# Define entrypoint and gunicorn running on port 8000
EXPOSE 8000
COPY docker/entrypoint.sh /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# Additional steps
ONBUILD ARG APPS
ONBUILD ARG BUILD_DEPS
ONBUILD ARG SYSTEM_DEPS
ONBUILD USER 0:0
ONBUILD RUN set -e; \
if [ -n "$BUILD_DEPS" ]; then \
eatmydata apt-get update; \
eatmydata apt-get install -y $BUILD_DEPS; \
fi; \
if [ -n "$SYSTEM_DEPS" ]; then \
eatmydata apt-get update; \
eatmydata apt-get install -y $SYSTEM_DEPS; \
fi;
ONBUILD RUN set -e; \
if [ -n "$APPS" ]; then \
eatmydata pip install $APPS; \
fi; \
eatmydata aleksis-admin vite build; \
eatmydata aleksis-admin compile_scss; \
eatmydata aleksis-admin collectstatic --no-input --clear; \
rm -rf /usr/local/share/.cache; \
eatmydata apt-get remove --purge -y yarnpkg $BUILD_DEPS; \
eatmydata apt-get autoremove --purge -y; \
apt-get clean -y; \
rm -f /var/lib/apt/lists/*_*; \
rm -rf /root/.cache
ONBUILD USER 33:33
......@@ -121,7 +121,7 @@ been modified and the date of modification.
of the Original Works or Derivative Works, this Distribution or
Communication will be done under the terms of this Licence or of a
later version of this Licence unless the Original Work is expressly
distributed only under this version of the Licencefor example by
distributed only under this version of the Licencefor example by
communicating ‘EUPL v. 1.2 only’. The Licensee (becoming Licensor)
cannot offer or impose any additional terms or conditions on the Work
or Derivative Work that alter or restrict the terms of the Licence.
......@@ -306,7 +306,7 @@ Appendix
* Creative Commons Attribution-ShareAlike v. 3.0 Unported
(CC BY-SA 3.0) for works other than software
* European Union Public Licence (EUPL) v. 1.1, v. 1.2
* Québec Free and Open-Source LicenceReciprocity (LiLiQ-R)
* Québec Free and Open-Source LicenceReciprocity (LiLiQ-R)
or Strong Reciprocity (LiLiQ-R+)
The European Commission may update this Appendix to later versions of
......
include CODE_OF_CONDUCT.rst
include CONTRIBUTING.rst
include LICENCE.rst
include manage.py
recursive-include aleksis/core/static *
recursive-include aleksis/core/templates *
recursive-include aleksis/core/migrations *
recursive-include docs *
AlekSIS (School Information System) — Core
==========================================
AlekSIS (School Information System) — Core (Core functionality and app framework)
=================================================================================
Warning
-------
This is the core of the AlekSIS framework and the official distribution
(see below). It bundles functionality for all apps, and utilities for
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®`_
website or the distribution repository on `EduGit`_.
**This is a preview version of AlekSIS. Do not use with sensitive data. Especially, do not grant access to students yet.**
Features
--------
The AlekSIS core currently provides the following features:
What AlekSIS is
----------------
* For users:
AlekSIS is a web-based school information system (SIS) which can be used to
manage and/or publish organisational subjects of educational institutions.
* Authentication via local account, LDAP, or social accounts
* Two factor authentication via WebAuthn, OTP, or SMS
* Configurable dashboard with widgets
* User-specific preferences
* Global search
* Global calendar system
* CalDAV and CardDAV support
* Manage personal events
* Manage persons
* Manage groups and group types
* Manage roles per group
* Manage announcements
* Manage holidays
* Notifications via SMS, email, or dashboard
* PWA with offline caching
* User registration, password changes and password reset
* User invitations with invite codes and targeted invites
It was originally developed together with Städt. Leibniz-Gymnasium Remscheid
as a proprietary product. Five years after the school stole the original
code base, as a complete re-implementation as well-designed, free and open
source software, BiscuIT-ng was started. In the meantime, students from the
Katharineum in Lübeck implemented School-Apps with the same goals and tools.
In 2020, BiscuIT-ng and School-Apps were combined into AlekSIS.
* For admins
AlekSIS is a platform based on Django, that provides central funstions
and data structures that can be used by apps that are developed and provided
seperately. The core can interact closely with the Debian Edu / Skolelinux
system.
* `aleksis-admin` script to wrap django-admin with pre-configured settings
* Manage school terms
* Custom menu entries (e.g. in footer)
* Automatic backup of database, static and media files
* OAuth2 and OpenID Connect provider support
* Serve prometheus metrics
* System health and data checks
* Configuration of low-level settings via configuration files
* System-wide preferenes
* Creating dashboard widgets for external links/apps
Core features
--------------
* For developers
TBA.
* Generic PDF generation with firefox
* Caching with Valkey
* Django REST framework for apps to use at own discretion
* Injection of fields, methods, permissions and properties via custom ``ExtensibleModel``
* K8s compatible, read-only Docker image
* Object-level permissions and rules with ``django-guardian`` and ``django-rules``
* uWSGI and Celery via ``django-uwsgi`` in development
* Extensible dashbaord widget system
* Extensible calendar system
* Extensible OAuth/OpenID Connect scope and claims system
Licence
-------
::
Copyright © 2017, 2018, 2019, 2020 Jonathan Weth <wethjo@katharineum.de>
Copyright © 2017, 2018, 2019 Frank Poetzsch-Heffter <p-h@katharineum.de>
Copyright © 2018, 2019, 2020 Hangzhi Yu <yuha@katharineum.de>
Copyright © 2018, 2019 Julian Leucker <leuckeju@katharineum.de>
Copyright © 2019, 2020 Dominik George <dominik.george@teckids.org>
Copyright © 2019, 2020 mirabilos <thorsten.glaser@teckids.org>
Copyright © 2019, 2020 Tom Teichler <tom.teichler@teckids.org>
Copyright © 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Jonathan Weth <dev@jonathanweth.de>
Copyright © 2017, 2018, 2019, 2020 Frank Poetzsch-Heffter <p-h@katharineum.de>
Copyright © 2018, 2019, 2020, 2021, 2022, 2023, 2024 Hangzhi Yu <yuha@katharineum.de>
Copyright © 2018, 2019, 2020, 2021, 2022, 2023, 2024 Julian Leucker <leuckeju@katharineum.de>
Copyright © 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dominik George <dominik.george@teckids.org>
Copyright © 2019, 2020, 2021, 2022 Tom Teichler <tom.teichler@teckids.org>
Copyright © 2019 mirabilos <thorsten.glaser@teckids.org>
Copyright © 2021, 2022, 2023, 2024 magicfelix <felix@felix-zauberer.de>
Copyright © 2021 Lloyd Meins <meinsll@katharineum.de>
Copyright © 2022 Benedict Suska <benedict.suska@teckids.org>
Copyright © 2022, 2023, 2024 Lukas Weichelt <lukas.weichelt@teckids.org>
Copyright © 2023, 2024 Michael Bauer <michael-bauer@posteo.de>
Copyright © 2024 Jonathan Krüger <jonathan.krueger@teckids.org>
Licenced under the EUPL, version 1.2 or later
Licenced under the EUPL, version 1.2 or later, by Teckids e.V. (Bonn, Germany).
Please see the LICENCE.rst file accompanying this distribution for the
full licence text or on the `European Union Public Licence`_ website
https://joinup.ec.europa.eu/collection/eupl/guidelines-users-and-developers
(including all other official language versions).
.. _AlekSIS: https://edugit.org/AlekSIS/AlekSIS
Trademark
---------
AlekSIS® is a registered trademark of the AlekSIS open source project, represented
by Teckids e.V. Please refer to the `trademark policy`_ for hints on using the trademark
AlekSIS®.
.. _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
import pkg_resources
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__ = pkg_resources.get_distribution("AlekSIS").version
__version__ = metadata.distribution("AlekSIS-Core").version
except Exception:
__version__ = "unknown"
default_app_config = "aleksis.core.apps.CoreConfig"
"""Management utilities for an AlekSIS installation."""
import os
import sys
import django.__main__
from django.core.management import execute_from_command_line
def aleksis_cmd():
"""Run django-admin command with correct settings path."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "aleksis.core.settings")
sys.argv[0] = django.__main__.__file__
execute_from_command_line(sys.argv)
if __name__ == "__main__":
sys.exit(aleksis_cmd())
from django.contrib import admin
from .models import Group, Person, School, SchoolTerm, Activity, Notification
admin.site.register(Person)
admin.site.register(Group)
admin.site.register(School)
admin.site.register(SchoolTerm)
admin.site.register(Activity)
admin.site.register(Notification)
from hattori.base import BaseAnonymizer, faker
from .models import Person
class PersonAnonymizer(BaseAnonymizer):
model = Person
attributes = [
("first_name", faker.first_name),
("last_name", faker.last_name),
("additional_name", ""),
("short_name", lambda **kwargs: faker.pystr(min_chars=3, max_chars=5, **kwargs)),
("street", faker.street_name),
("housenumber", faker.building_number),
("postal_code", faker.postcode),
("place", faker.city),
("phone_number", ""),
("mobile_number", ""),
("email", faker.email),
(
"date_of_birth",
lambda **kwargs: faker.date_of_birth(minimum_age=8, maximum_age=66, **kwargs),
),
("photo", ""),
]
from django.apps import AppConfig, apps
from typing import TYPE_CHECKING, Any, Optional
from constance.signals import config_updated
import django.apps
from django.conf import settings
from django.contrib import messages
from django.http import HttpRequest
from django.utils.module_loading import autodiscover_modules
from django.utils.translation import gettext as _
from .signals import clean_scss
from dynamic_preferences.registries import preference_models
from health_check.plugins import plugin_dir
from oauthlib.common import Request as OauthlibRequest
from .registries import group_preferences_registry, person_preferences_registry
from .util.apps import AppConfig
from .util.core_helpers import (
create_default_celery_schedule,
get_or_create_favicon,
get_site_preferences,
has_person,
)
from .util.types import setup_types
if TYPE_CHECKING:
from django.contrib.auth.models import User
class CoreConfig(AppConfig):
name = "aleksis.core"
verbose_name = "AlekSIS — The Free School Information System"
dist_name = "AlekSIS-Core"
urls = {
"Repository": "https://edugit.org/AlekSIS/official/AlekSIS/",
}
licence = "EUPL-1.2+"
copyright_info = (
(
[2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024],
"Jonathan Weth",
"dev@jonathanweth.de",
),
([2017, 2018, 2019, 2020], "Frank Poetzsch-Heffter", "p-h@katharineum.de"),
([2018, 2019, 2020, 2021, 2022, 2023, 2024], "Hangzhi Yu", "yuha@katharineum.de"),
([2018, 2019, 2020, 2021, 2022, 2023, 2024], "Julian Leucker", "leuckeju@katharineum.de"),
(
[2019, 2020, 2021, 2022, 2023, 2024, 2025],
"Dominik George",
"dominik.george@teckids.org",
),
([2019, 2020, 2021, 2022], "Tom Teichler", "tom.teichler@teckids.org"),
([2019], "mirabilos", "thorsten.glaser@teckids.org"),
([2021, 2022, 2023, 2024], "magicfelix", "felix@felix-zauberer.de"),
([2021], "Lloyd Meins", "meinsll@katharineum.de"),
([2022], "Benedict Suska", "benedict.suska@teckids.org"),
([2022, 2023, 2024], "Lukas Weichelt", "lukas.weichelt@teckids.org"),
([2023, 2024], "Michael Bauer", "michael-bauer@posteo.de"),
([2024], "Jonathan Krüger", "jonathan.krueger@teckids.org"),
)
def ready(self):
super().ready()
setup_types()
from django.conf import settings # noqa
# Autodiscover various modules defined by AlekSIS
autodiscover_modules("model_extensions", "form_extensions", "checks", "util.dav_handler")
personpreferencemodel = self.get_model("PersonPreferenceModel")
grouppreferencemodel = self.get_model("GroupPreferenceModel")
preference_models.register(personpreferencemodel, person_preferences_registry)
preference_models.register(grouppreferencemodel, group_preferences_registry)
from .health_checks import (
BackupJobHealthCheck,
DataChecksHealthCheckBackend,
DbBackupAgeHealthCheck,
MediaBackupAgeHealthCheck,
)
plugin_dir.register(DataChecksHealthCheckBackend)
plugin_dir.register(DbBackupAgeHealthCheck)
plugin_dir.register(MediaBackupAgeHealthCheck)
plugin_dir.register(BackupJobHealthCheck)
def preference_updated(
self,
sender: Any,
section: Optional[str] = None,
name: Optional[str] = None,
old_value: Optional[Any] = None,
new_value: Optional[Any] = None,
**kwargs,
) -> None:
from django.conf import settings # noqa
if section == "theme" and name in ("favicon", "pwa_icon"):
from favicon.models import Favicon, FaviconImg # noqa
is_favicon = name == "favicon"
if new_value:
# Get file object from preferences instead of using new_value
# to prevent problems with special file storages
file_obj = get_site_preferences()[f"{section}__{name}"]
favicon = Favicon.objects.update_or_create(
title=name,
defaults={"isFavicon": is_favicon, "faviconImage": file_obj},
)[0]
FaviconImg.objects.filter(faviconFK=favicon).delete()
else:
Favicon.objects.filter(title=name, isFavicon=is_favicon).delete()
if name in settings.DEFAULT_FAVICON_PATHS:
get_or_create_favicon(
name, settings.DEFAULT_FAVICON_PATHS[name], is_favicon=is_favicon
)
def pre_migrate(
self,
app_config: django.apps.AppConfig,
verbosity: int,
interactive: bool,
using: str,
plan: list[tuple],
apps: django.apps.registry.Apps,
**kwargs,
) -> None:
super().pre_migrate(app_config, verbosity, interactive, using, plan, apps)
from .data_checks import check_data_for_migrations
# Run data checks to validate data
check_data_for_migrations(with_dependencies=True)
def post_migrate(
self,
app_config: django.apps.AppConfig,
verbosity: int,
interactive: bool,
using: str,
**kwargs,
) -> None:
from django.conf import settings # noqa
from .data_checks import check_data_for_migrations
super().post_migrate(app_config, verbosity, interactive, using, **kwargs)
# Ensure that default Favicon object exists
for name, default in settings.DEFAULT_FAVICON_PATHS.items():
get_or_create_favicon(name, default, is_favicon=name == "favicon")
# Create default periodic tasks
create_default_celery_schedule()
# Run data checks to validate data
check_data_for_migrations()
def user_logged_in(
self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs
) -> None:
if has_person(user):
# Save the associated person to pick up defaults
user.person.save()
def user_logged_out(
self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs
) -> None:
messages.success(request, _("You have been logged out successfully."))
@classmethod
def get_all_scopes(cls) -> dict[str, str]:
scopes = {
"read": "Read anything the resource owner can read",
"write": "Write anything the resource owner can write",
}
if settings.OAUTH2_PROVIDER.get("OIDC_ENABLED", False):
scopes |= {
"openid": _("OpenID Connect scope"),
"profile": _("Given name, family name, link to profile and picture if existing."),
"addresses": _("Postal addresses"),
"email": _("Email address"),
"phone": _("Home and mobile phone"),
"groups": _("Groups"),
}
return scopes
@classmethod
def get_additional_claims(cls, scopes: list[str], request: OauthlibRequest) -> dict[str, Any]:
django_request = HttpRequest()
django_request.META = request.headers
claims = {
"preferred_username": request.user.username,
}
if "profile" in scopes:
if has_person(request.user):
claims["given_name"] = request.user.person.first_name
claims["family_name"] = request.user.person.last_name
claims["profile"] = django_request.build_absolute_uri(
request.user.person.get_absolute_url()
)
if request.user.person.avatar:
claims["picture"] = django_request.build_absolute_uri(
request.user.person.avatar.url
)
else:
claims["given_name"] = request.user.first_name
claims["family_name"] = request.user.last_name
if "email" in scopes:
if has_person(request.user):
claims["email"] = request.user.person.email
else:
claims["email"] = request.user.email
if "addresses" in scopes and has_person(request.user):
claims["addresses"] = [
{
"street_address": address.street + " " + address.housenumber,
"locality": address.place,
"postal_code": address.postal_code,
}
for address in request.user.person.addresses.all()
]
if "phone" in scopes and has_person(request.user):
claims["mobile_number"] = request.user.person.mobile_number
claims["phone_number"] = request.user.person.phone_number
if "groups" in scopes and has_person(request.user):
claims["groups"] = list(
request.user.person.member_of.values_list("name", flat=True).all()
)
def ready(self) -> None:
clean_scss()
config_updated.connect(clean_scss)
return claims