Compare commits

...

115 Commits

Author SHA1 Message Date
Joel Falknau
63fb449060 django now doesnt like unsaved models, fair 2024-08-24 15:42:11 +10:00
Joel Falknau
235675fa9b django.utils.timezone.utc was removed. (shame it was handy) 2024-08-24 15:41:37 +10:00
Joel Falknau
a065f043eb correct index for new page name 2024-08-24 15:09:12 +10:00
Joel Falknau
0839920032 use defined pygments 2024-08-24 15:09:02 +10:00
Joel Falknau
29ad4acff7 bring docs up to python 312 across the board 2024-08-24 15:08:41 +10:00
Joel Falknau
3a5b84d1f9 remove \u2060 from image name 2024-08-24 13:42:55 +10:00
Joel Falknau
bbcb94021e run pyupgrade 2024-08-23 13:55:25 +10:00
Joel Falknau
d50f13528b add python 3.13 RC to tox allow fail 2024-08-22 12:58:54 +10:00
Joel Falknau
c88521af88 django-celery-beat >=2.7.0 2024-08-22 12:54:31 +10:00
Joel Falknau
2bd5ff8723 Merge branch 'master' of gitlab.com:allianceauth/allianceauth into v5.x 2024-08-12 13:23:34 +10:00
Joel Falknau
84484cebcb move to django 5.1.x 2024-08-12 13:20:27 +10:00
Joel Falknau
5ee34fcb2d bring docker major up 2024-08-12 13:20:11 +10:00
Joel Falknau
046473def1 use 3.12 in builds 2024-08-12 13:19:57 +10:00
Joel Falknau
6aaba2bf3d Drop Python 3.8 and 3.9 2024-08-12 13:19:08 +10:00
Joel Falknau
acff3695bc Version Bump 4.2.2 2024-08-06 12:43:06 +10:00
Ariel Rin
43ec8514aa Merge branch 'improve-redis-installation-on-ubuntu' into 'master'
[MISC] Improve Redis installation instructions for Ubuntu

See merge request allianceauth/allianceauth!1641
2024-08-05 01:26:07 +00:00
Peter Pfeufer
4c629b193f [CHANGE] Redis hint link 2024-08-05 03:21:14 +02:00
Peter Pfeufer
c651da4011 [MISC] Improve Redis installation instructions for Ubuntu 2024-08-05 03:12:24 +02:00
Ariel Rin
da382cffd1 Merge branch 'fix-mumble-url' into 'master'
[FIX] Mumble URL in service card

See merge request allianceauth/allianceauth!1637
2024-08-05 01:04:24 +00:00
Ariel Rin
4ecfc3afd8 Merge branch 'docs-docker-headlines' into 'master'
[CHANGE] Clarify that these instructions are for Docker

See merge request allianceauth/allianceauth!1638
2024-08-05 01:02:57 +00:00
Ariel Rin
4eb7dbbe62 Merge branch 'add-margin' into 'master'
[ADD] A bit margin to the notifications

See merge request allianceauth/allianceauth!1639
2024-08-05 01:02:52 +00:00
Ariel Rin
c96ba65296 Merge branch 'avoid-KeyError-in-checks' into 'master'
[FIX] Avoid `KeyError` in `celery_settings` checks

See merge request allianceauth/allianceauth!1640
2024-08-05 01:02:40 +00:00
Peter Pfeufer
ff2f60f7f3 [FIX] Avoid KeyError in celery_settings checks 2024-08-04 18:29:43 +02:00
Peter Pfeufer
3000545c98 [ADD] A bit margin to the notifications 2024-07-26 23:09:15 +02:00
Peter Pfeufer
f3ad092ef2 [CHANGNE] Clarify that these instructions are for Docker
This should also fix the menu item title, hopefully …
2024-07-19 19:13:48 +02:00
Peter Pfeufer
a012e7df2f [FIX] Mumble URL in service card 2024-07-19 17:33:32 +02:00
Ariel Rin
1fa77412c0 Merge branch 'fix/missing-celery-setting' into 'master'
fix missing setting in celery

See merge request allianceauth/allianceauth!1636
2024-07-15 13:39:28 +00:00
Matteo Ghia
e56caeb22b fix missing setting in celery 2024-07-15 14:53:45 +02:00
Joel Falknau
ceb07ebc67 Version Bump 4.2.1 2024-07-15 22:20:32 +10:00
Ariel Rin
237075d45c Merge branch 'checklib' into 'master'
More Detail on the SQL charset check

See merge request allianceauth/allianceauth!1635
2024-07-15 12:18:47 +00:00
Ariel Rin
7099b1946d More Detail on the SQL charset check 2024-07-15 12:18:47 +00:00
Joel Falknau
e416ab8ff2 Version Bump 4.2.0 2024-07-15 21:25:00 +10:00
Joel Falknau
2802ed03a5 Add Celery to classifiers 2024-07-15 21:22:58 +10:00
Ariel Rin
4af73c76fe Merge branch 'analytics' into 'master'
Analytics

See merge request allianceauth/allianceauth!1632
2024-07-15 11:00:27 +00:00
Ariel Rin
b6149979aa Analytics 2024-07-15 11:00:26 +00:00
Ariel Rin
cb20288427 Merge branch 'checklib' into 'master'
More Checks for System Packages and Configs

See merge request allianceauth/allianceauth!1633
2024-07-15 11:00:06 +00:00
Ariel Rin
db6f4c91dc More Checks for System Packages and Configs 2024-07-15 11:00:06 +00:00
Ariel Rin
57ac7a5277 Merge branch 'center-error-messages' into 'master'
[CHANGE] Center HTTPError messages

See merge request allianceauth/allianceauth!1634
2024-07-15 03:32:34 +00:00
Peter Pfeufer
136438f9c2 [CHANGE] Center HTTPError messages 2024-07-13 00:31:44 +02:00
Ariel Rin
e2be8b3440 Merge branch 'composelint' into 'master'
Optimize Dockerfile

See merge request allianceauth/allianceauth!1630
2024-06-17 08:42:39 +00:00
Ariel Rin
04f3473ef3 Optimize Dockerfile 2024-06-17 08:42:39 +00:00
Ariel Rin
255cb0da8d Merge branch 'bootstrap-class-fixes' into 'master'
[CHANGE] Remove unnecessary bootstrap classes from the dashboard

See merge request allianceauth/allianceauth!1631
2024-06-17 08:42:34 +00:00
Peter Pfeufer
069352fb0f [FIX] Switch to bottom margin instead of top/bottom padding
This is the much more commonly used approach.
2024-06-01 12:01:44 +02:00
Peter Pfeufer
66e8ddb684 [CHANGE] Remove unnecessary bootstrap classes from the dashboard 2024-05-31 21:59:24 +02:00
Ariel Rin
179c26975c Version Bump 4.1.0 2024-05-27 14:31:41 +10:00
Ariel Rin
e17f6e799b correct capitalization 2024-05-27 14:31:21 +10:00
Ariel Rin
7cd8294104 Merge branch 'widget-title-to-framework' into 'master'
[ADD] Widget title partial to AA framework

See merge request allianceauth/allianceauth!1629
2024-05-27 04:25:56 +00:00
Peter Pfeufer
ede5540335 [ADD] Widget title partial to AA framework 2024-05-26 16:52:15 +02:00
Ariel Rin
747279b773 Merge branch 'yet-another-padding-fix' into 'master'
Some more widget fixes

See merge request allianceauth/allianceauth!1628
2024-05-26 09:08:00 +00:00
Peter Pfeufer
44f8b1c477 [FIX] Don't try to underline these links
Fixing this while I'm at it …
2024-05-26 10:06:07 +02:00
Peter Pfeufer
7c6ebd9bf6 [FIX] Yet another padding in the dashboard widgets 2024-05-26 09:52:03 +02:00
Ariel Rin
430469b708 Merge branch 'widget-padding-fix' into 'master'
[FIX] Dashboard widget padding

See merge request allianceauth/allianceauth!1627
2024-05-26 06:51:20 +00:00
Peter Pfeufer
efbb3cee31 [FIX] Dashboard widget padding 2024-05-26 08:23:57 +02:00
Ariel Rin
21094ed4dd lint 2024-05-26 13:30:21 +10:00
Ariel Rin
5f326efc7e Merge branch 'v4docs' into 'master'
Expand Tuning Doccs

See merge request allianceauth/allianceauth!1624
2024-05-26 03:22:08 +00:00
Ariel Rin
b6e34ace35 Expand Tuning Doccs 2024-05-26 03:22:08 +00:00
Ariel Rin
fe4a8965e3 Merge branch 'master' into 'master'
Improve Error Troubleshooting with Log Documentation

See merge request allianceauth/allianceauth!1619
2024-05-26 02:58:23 +00:00
Ariel Rin
23371c233d Merge branch 'bmtuk-master-patch-12042' into 'master'
Timerboard - Fix Timer deletion

See merge request allianceauth/allianceauth!1625
2024-05-26 02:56:35 +00:00
Ariel Rin
7a3bbf0d7f Merge branch 'staff-menu-item' into 'master'
Allow staff access to admin button in user menu

See merge request allianceauth/allianceauth!1626
2024-05-26 02:56:05 +00:00
Aaron Kable
89a1bec9c1 Allow staff access to admin button in user menu 2024-05-26 02:56:04 +00:00
Ben Thomas
1c1e70619a Fix ability to delete timers 2024-05-21 23:42:19 +00:00
Ariel Rin
0ff4374efa Version Bump 4.0.2 2024-05-12 19:51:02 +10:00
Ariel Rin
18d0e58a48 Merge branch 'master' of gitlab.com:allianceauth/allianceauth 2024-05-12 19:50:10 +10:00
Ariel Rin
84f44338dc Merge branch 'mute-subtitle' into 'master'
[CHANGE] Mute subtitle text

See merge request allianceauth/allianceauth!1623
2024-05-12 09:48:47 +00:00
Ariel Rin
2ba0412890 Add Polish to language selections 2024-05-12 19:44:30 +10:00
Peter Pfeufer
2326522b29 [CHANGE] Docs updated with the missing subtitle attribute 2024-05-12 11:40:47 +02:00
Peter Pfeufer
a7cb6ee434 [CHANGE] Mute subtitle text 2024-05-12 11:35:42 +02:00
Ariel Rin
2aeef63565 Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1609
2024-05-12 09:23:26 +00:00
Ariel Rin
3c9e7335ef Updates for project Alliance Auth 2024-05-12 09:23:26 +00:00
Ariel Rin
49067de325 apply precommit here 2024-05-12 19:21:58 +10:00
Ariel Rin
471e7e29ae Update Source EN 2024-05-12 19:15:56 +10:00
Ariel Rin
3ec5775406 remove unneeded legacy transifex config 2024-05-12 19:15:29 +10:00
Ariel Rin
e804d2b60d update (and sort) precommit 2024-05-12 19:15:14 +10:00
Ariel Rin
742438a95d Merge branch 'timerboard-bg-fix' into 'master'
Make table bg a little less oppressive in timerboard

See merge request allianceauth/allianceauth!1617
2024-04-27 05:09:37 +00:00
Ariel Rin
5c60086baa Merge branch 'add-missing-padding' into 'master'
[ADD] missing padding

See merge request allianceauth/allianceauth!1620
2024-04-27 05:05:22 +00:00
Ariel Rin
e49041bb14 Merge branch 'Haffi-master-patch-21354' into 'master'
updates package registry reference

Closes #1403

See merge request allianceauth/allianceauth!1621
2024-04-25 09:48:10 +00:00
Haffi
f3cbe91883 updates package registry reference 2024-04-25 08:42:18 +00:00
Peter Pfeufer
ea439a2176 [FIX] Indentation 2024-04-15 17:16:01 +02:00
Peter Pfeufer
56e1e76f11 [ADD] Missing theme padding on public pages 2024-04-15 17:15:35 +02:00
entropylaw
634e7357be Update troubleshooting.md. Fixed Typo. 2024-04-13 21:21:13 -05:00
entropylaw
08dc88da1a Update troubleshooting.md 2024-04-13 20:59:19 -05:00
entropylaw
3d206e445c Update troubleshooting.md 2024-04-13 20:55:10 -05:00
entropylaw
64686cdad1 Update troubleshooting.md 2024-04-13 20:49:46 -05:00
AnomicDev
d7fe09bdf1 Update troubleshooting.md 2024-04-14 01:19:51 +00:00
AnomicDev
6da50da92f Update troubleshooting.md 2024-04-14 01:10:38 +00:00
AnomicDev
51e4dd986f Update troubleshooting.md 2024-04-14 01:09:56 +00:00
AnomicDev
bee6522182 Update troubleshooting.md 2024-04-14 01:07:08 +00:00
AnomicDev
1711a9dd33 Update troubleshooting.md 2024-04-14 01:05:25 +00:00
AnomicDev
3914626379 Update troubleshooting.md 2024-04-14 01:04:59 +00:00
AnomicDev
df276cb32d Update troubleshooting.md 2024-04-14 00:59:19 +00:00
AnomicDev
daad7d8b10 Update troubleshooting.md 2024-04-14 00:58:44 +00:00
AnomicDev
3bf5bc0fe3 Update troubleshooting.md 2024-04-14 00:58:09 +00:00
AnomicDev
96abae553a Update troubleshooting.md 2024-04-14 00:57:12 +00:00
AnomicDev
f9cbfb1562 Update troubleshooting.md 2024-04-14 00:55:59 +00:00
AnomicDev
8eaa94e179 Update troubleshooting.md 2024-04-14 00:55:24 +00:00
AnomicDev
4f876b648b Update troubleshooting.md 2024-04-14 00:53:16 +00:00
AnomicDev
cd738137c0 Update troubleshooting.md 2024-04-14 00:52:07 +00:00
Aaron Kable
5605eb129d Make table bg a little less opressive in timerboard 2024-04-10 16:27:09 +08:00
Ariel Rin
87ef0f21a3 Merge branch 'add-missing-colon' into 'master'
[ADD] Missing colon

See merge request allianceauth/allianceauth!1616
2024-04-10 01:12:30 +00:00
Ariel Rin
a1c7ce827e Merge branch 'topnav-revival' into 'master'
V4 Theme - Re-add the Top Nav for unauthenticated users

See merge request allianceauth/allianceauth!1615
2024-04-10 01:09:03 +00:00
Aaron Kable
97466bcdfb V4 Theme - Re-add the Top Nav for unauthenticated users 2024-04-10 01:09:02 +00:00
Peter Pfeufer
ff3096b106 [ADD] Missing colon 2024-04-07 15:58:35 +02:00
Ariel Rin
98f0d77f3f Merge branch 'pre-commit-updates' into 'master'
[MISC] pre-commit config updates

See merge request allianceauth/allianceauth!1614
2024-03-29 02:24:49 +00:00
Peter Pfeufer
92548ba402 [ADD] pyproject.toml validation 2024-03-28 20:40:45 +01:00
Peter Pfeufer
c46741d311 [MISC] Update repo versions 2024-03-28 20:34:31 +01:00
Peter Pfeufer
7c7c1abf7c [CHANGE] Project file linter
We don't use setup.cfg anymore, so we should lint the pyproject.toml file now.
2024-03-28 20:33:16 +01:00
Ariel Rin
fc303b1b0a Merge branch 'fix-phpbb3-mysql-user' into 'master'
[FIX] DB user for phpBB

See merge request allianceauth/allianceauth!1613
2024-03-26 10:19:07 +00:00
Peter Pfeufer
4e220a9679 [FIX] DB user for phpBB 2024-03-24 17:08:34 +01:00
Ariel Rin
b17b1f7504 Version Bump 4.0.1 2024-03-21 19:34:02 +10:00
Ariel Rin
7081fc0e76 Merge branch 'docs' into 'master'
Documentation

See merge request allianceauth/allianceauth!1610
2024-03-21 09:17:43 +00:00
Ariel Rin
68e4574f19 Merge branch 'analytics' into 'master'
Pass Version to analytics user properties

See merge request allianceauth/allianceauth!1612
2024-03-21 09:12:53 +00:00
Ariel Rin
e6e0a70012 Pass Version to analytics user properties 2024-03-21 09:12:53 +00:00
Ariel Rin
13e38da942 Merge branch 'timers-row-background' into 'master'
[CHANGE] Restore the "old" color coding in table rows

See merge request allianceauth/allianceauth!1611
2024-03-21 09:12:25 +00:00
Peter Pfeufer
468c1de26b [CHANGE] restore the "old" color coding in table rows 2024-03-20 19:22:03 +01:00
Ariel Rin
22ef5ac0e5 raw file contents incase someone wgets 2024-03-16 15:44:39 +10:00
98 changed files with 10186 additions and 962 deletions

View File

@@ -25,7 +25,7 @@ before_script:
pre-commit-check: pre-commit-check:
<<: *only-default <<: *only-default
stage: pre-commit stage: pre-commit
image: python:3.11-bullseye image: python:3.11-bookworm
# variables: # variables:
# PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit # PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
# cache: # cache:
@@ -51,33 +51,9 @@ secret_detection:
stage: gitlab stage: gitlab
before_script: [] before_script: []
test-3.8-core:
<<: *only-default
image: python:3.8-bullseye
script:
- tox -e py38-core
artifacts:
when: always
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
test-3.9-core:
<<: *only-default
image: python:3.9-bullseye
script:
- tox -e py39-core
artifacts:
when: always
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
test-3.10-core: test-3.10-core:
<<: *only-default <<: *only-default
image: python:3.10-bullseye image: python:3.10-bookworm
script: script:
- tox -e py310-core - tox -e py310-core
artifacts: artifacts:
@@ -89,7 +65,7 @@ test-3.10-core:
test-3.11-core: test-3.11-core:
<<: *only-default <<: *only-default
image: python:3.11-bullseye image: python:3.11-bookworm
script: script:
- tox -e py311-core - tox -e py311-core
artifacts: artifacts:
@@ -101,7 +77,7 @@ test-3.11-core:
test-3.12-core: test-3.12-core:
<<: *only-default <<: *only-default
image: python:3.12-rc-bullseye image: python:3.12-bookworm
script: script:
- tox -e py312-core - tox -e py312-core
artifacts: artifacts:
@@ -111,33 +87,22 @@ test-3.12-core:
coverage_format: cobertura coverage_format: cobertura
path: coverage.xml path: coverage.xml
test-3.8-all: test-3.13-core:
<<: *only-default <<: *only-default
image: python:3.8-bullseye image: python:3.13-rc-bookworm
script: script:
- tox -e py38-all - tox -e py313-core
artifacts:
when: always
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
test-3.9-all:
<<: *only-default
image: python:3.9-bullseye
script:
- tox -e py39-all
artifacts: artifacts:
when: always when: always
reports: reports:
coverage_report: coverage_report:
coverage_format: cobertura coverage_format: cobertura
path: coverage.xml path: coverage.xml
allow_failure: true
test-3.10-all: test-3.10-all:
<<: *only-default <<: *only-default
image: python:3.10-bullseye image: python:3.10-bookworm
script: script:
- tox -e py310-all - tox -e py310-all
artifacts: artifacts:
@@ -149,7 +114,7 @@ test-3.10-all:
test-3.11-all: test-3.11-all:
<<: *only-default <<: *only-default
image: python:3.11-bullseye image: python:3.11-bookworm
script: script:
- tox -e py311-all - tox -e py311-all
artifacts: artifacts:
@@ -162,7 +127,7 @@ test-3.11-all:
test-3.12-all: test-3.12-all:
<<: *only-default <<: *only-default
image: python:3.12-rc-bullseye image: python:3.12-bookworm
script: script:
- tox -e py312-all - tox -e py312-all
artifacts: artifacts:
@@ -172,9 +137,22 @@ test-3.12-all:
coverage_format: cobertura coverage_format: cobertura
path: coverage.xml path: coverage.xml
test-3.13-all:
<<: *only-default
image: python:3.13-rc-bookworm
script:
- tox -e py313-all
artifacts:
when: always
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
allow_failure: true
build-test: build-test:
stage: test stage: test
image: python:3.11-bullseye image: python:3.12-bookworm
before_script: before_script:
- python -m pip install --upgrade pip - python -m pip install --upgrade pip
@@ -193,13 +171,13 @@ build-test:
test-docs: test-docs:
<<: *only-default <<: *only-default
image: python:3.11-bullseye image: python:3.12-bookworm
script: script:
- tox -e docs - tox -e docs
deploy_production: deploy_production:
stage: deploy stage: deploy
image: python:3.11-bullseye image: python:3.12-bookworm
before_script: before_script:
- python -m pip install --upgrade pip - python -m pip install --upgrade pip
@@ -215,10 +193,10 @@ deploy_production:
build-image: build-image:
before_script: [] before_script: []
image: docker:24.0 image: docker:27.0
stage: docker stage: docker
services: services:
- docker:24.0-dind - docker:27-dind
script: | script: |
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -) CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_SHORT_SHA IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_SHORT_SHA
@@ -239,10 +217,10 @@ build-image:
build-image-dev: build-image-dev:
before_script: [] before_script: []
image: docker:24.0 image: docker:27
stage: docker stage: docker
services: services:
- docker:24.0-dind - docker:27-dind
script: | script: |
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -) CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
@@ -260,10 +238,10 @@ build-image-dev:
build-image-mr: build-image-mr:
before_script: [] before_script: []
image: docker:24.0 image: docker:27
stage: docker stage: docker
services: services:
- docker:24.0-dind - docker:27-dind
script: | script: |
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -) CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA

View File

@@ -4,8 +4,21 @@
# pre-commit autoupdate # pre-commit autoupdate
repos: repos:
# Code Upgrades
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.2
hooks:
- id: pyupgrade
args: [--py310-plus]
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.17.0
hooks:
- id: django-upgrade
args: [--target-version=4.2]
# Formatting
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 rev: v4.6.0
hooks: hooks:
# Identify invalid files # Identify invalid files
- id: check-ast - id: check-ast
@@ -13,27 +26,24 @@ repos:
- id: check-json - id: check-json
- id: check-toml - id: check-toml
- id: check-xml - id: check-xml
# git checks # git checks
- id: check-merge-conflict - id: check-merge-conflict
- id: check-added-large-files - id: check-added-large-files
args: [ --maxkb=1000 ] args: [--maxkb=1000]
- id: detect-private-key - id: detect-private-key
- id: check-case-conflict - id: check-case-conflict
# Python checks # Python checks
# - id: check-docstring-first # - id: check-docstring-first
- id: debug-statements - id: debug-statements
# - id: requirements-txt-fixer # - id: requirements-txt-fixer
- id: fix-encoding-pragma - id: fix-encoding-pragma
args: [ --remove ] args: [--remove]
- id: fix-byte-order-marker - id: fix-byte-order-marker
# General quality checks # General quality checks
- id: mixed-line-ending - id: mixed-line-ending
args: [ --fix=lf ] args: [--fix=lf]
- id: trailing-whitespace - id: trailing-whitespace
args: [ --markdown-linebreak-ext=md ] args: [--markdown-linebreak-ext=md]
exclude: | exclude: |
(?x)( (?x)(
\.min\.css| \.min\.css|
@@ -52,7 +62,6 @@ repos:
\.mo| \.mo|
swagger\.json swagger\.json
) )
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python - repo: https://github.com/editorconfig-checker/editorconfig-checker.python
rev: 2.7.3 rev: 2.7.3
hooks: hooks:
@@ -65,21 +74,26 @@ repos:
\.mo| \.mo|
swagger\.json swagger\.json
) )
- repo: https://github.com/igorshubovych/markdownlint-cli
- repo: https://github.com/asottile/pyupgrade rev: v0.41.0
rev: v3.15.1
hooks: hooks:
- id: pyupgrade - id: markdownlint
args: [ --py38-plus ] args:
- --disable=MD013
- repo: https://github.com/adamchainz/django-upgrade # Infrastructure
rev: 1.16.0 - repo: https://github.com/tox-dev/pyproject-fmt
rev: 2.1.3
hooks: hooks:
- id: django-upgrade - id: pyproject-fmt
args: [--target-version=4.2] name: pyproject.toml formatter
description: "Format the pyproject.toml file."
- repo: https://github.com/asottile/setup-cfg-fmt args:
rev: v2.5.0 - --indent=4
additional_dependencies:
- tox==4.15.0 # https://github.com/tox-dev/tox/releases/latest
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.18
hooks: hooks:
- id: setup-cfg-fmt - id: validate-pyproject
args: [ --include-version-classifiers ] name: Validate pyproject.toml
description: "Validate the pyproject.toml file."

View File

@@ -7,11 +7,11 @@ version: 2
# Set the version of Python and other tools you might need # Set the version of Python and other tools you might need
build: build:
os: ubuntu-22.04 os: ubuntu-24.04
apt_packages: apt_packages:
- redis - redis
tools: tools:
python: "3.11" python: "3.12"
jobs: jobs:
post_system_dependencies: post_system_dependencies:
- redis-server --daemonize yes - redis-server --daemonize yes

View File

@@ -1,10 +0,0 @@
[main]
host = https://app.transifex.com
lang_map = zh-Hans: zh_Hans
[o:alliance-auth:p:alliance-auth:r:django-po]
file_filter = allianceauth/locale/<lang>/LC_MESSAGES/django.po
source_file = allianceauth/locale/en/LC_MESSAGES/django.po
source_lang = en
type = PO
minimum_perc = 0

View File

@@ -5,7 +5,7 @@ manage online service access.
# This will make sure the app is always imported when # This will make sure the app is always imported when
# Django starts so that shared_task will use this app. # Django starts so that shared_task will use this app.
__version__ = '4.0.0' __version__ = '4.2.2'
__title__ = 'Alliance Auth' __title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth' __url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = f'{__title__} v{__version__}' NAME = f'{__title__} v{__version__}'

View File

@@ -9,6 +9,8 @@ from .utils import (
install_stat_tokens, install_stat_tokens,
install_stat_users) install_stat_users)
from allianceauth import __version__
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
BASE_URL = "https://www.google-analytics.com" BASE_URL = "https://www.google-analytics.com"
@@ -99,11 +101,38 @@ def analytics_daily_stats():
event_type='Stats') event_type='Stats')
for appconfig in apps.get_app_configs(): for appconfig in apps.get_app_configs():
analytics_event(namespace='allianceauth.analytics', if appconfig.label in [
task='send_extension_stats', "django_celery_beat",
label=appconfig.label, "bootstrapform",
value=1, "messages",
event_type='Stats') "sessions",
"auth",
"staticfiles",
"users",
"addons",
"admin",
"humanize",
"contenttypes",
"sortedm2m",
"django_bootstrap5",
"tokens",
"authentication",
"services",
"framework",
"notifications"
"eveonline",
"navhelper",
"analytics",
"menu",
"theme"
]:
pass
else:
analytics_event(namespace='allianceauth.analytics',
task='send_extension_stats',
label=appconfig.label,
value=1,
event_type='Stats')
@shared_task() @shared_task()
@@ -139,7 +168,7 @@ def send_ga_tracking_celery_event(
'client_id': AnalyticsIdentifier.objects.get(id=1).identifier.hex, 'client_id': AnalyticsIdentifier.objects.get(id=1).identifier.hex,
"user_properties": { "user_properties": {
"allianceauth_version": { "allianceauth_version": {
"value": "allianceauth_version" "value": __version__
} }
}, },
'non_personalized_ads': True, 'non_personalized_ads': True,

View File

@@ -5,26 +5,5 @@ from django.core.checks import Warning, Error, register
class AllianceAuthConfig(AppConfig): class AllianceAuthConfig(AppConfig):
name = 'allianceauth' name = 'allianceauth'
def ready(self) -> None:
@register() import allianceauth.checks # noqa
def check_settings(app_configs, **kwargs):
from django.conf import settings
errors = []
if hasattr(settings, "SITE_URL"):
if settings.SITE_URL[-1] == "/":
errors.append(Warning(
"'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth."))
else:
errors.append(Error(
"No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules."))
if hasattr(settings, "CSRF_TRUSTED_ORIGINS"):
if hasattr(settings, "SITE_URL"):
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS:
errors.append(Warning(
"'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified."))
else:
errors.append(Error(
"No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified"))
return errors

View File

@@ -12,7 +12,7 @@ from django.conf import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def active_tasks_count() -> Optional[int]: def active_tasks_count() -> int | None:
"""Return count of currently active tasks """Return count of currently active tasks
or None if celery workers are not online. or None if celery workers are not online.
""" """
@@ -20,7 +20,7 @@ def active_tasks_count() -> Optional[int]:
return _tasks_count(inspect.active()) return _tasks_count(inspect.active())
def _tasks_count(data: dict) -> Optional[int]: def _tasks_count(data: dict) -> int | None:
"""Return count of tasks in data from celery inspect API.""" """Return count of tasks in data from celery inspect API."""
try: try:
tasks = itertools.chain(*data.values()) tasks = itertools.chain(*data.values())
@@ -29,7 +29,7 @@ def _tasks_count(data: dict) -> Optional[int]:
return len(list(tasks)) return len(list(tasks))
def queued_tasks_count() -> Optional[int]: def queued_tasks_count() -> int | None:
"""Return count of queued tasks. Return None if there was an error.""" """Return count of queued tasks. Return None if there was an error."""
try: try:
with current_app.connection_or_acquire() as conn: with current_app.connection_or_acquire() as conn:

View File

@@ -2,7 +2,8 @@ from django.urls import include
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from functools import wraps from functools import wraps
from typing import Callable, Iterable, Optional from typing import Optional
from collections.abc import Callable, Iterable
from django.urls import include from django.urls import include
from django.contrib import messages from django.contrib import messages
@@ -17,7 +18,7 @@ def user_has_main_character(user):
def decorate_url_patterns( def decorate_url_patterns(
urls, decorator: Callable, excluded_views: Optional[Iterable] = None urls, decorator: Callable, excluded_views: Iterable | None = None
): ):
"""Decorate views given in url patterns except when they are explicitly excluded. """Decorate views given in url patterns except when they are explicitly excluded.

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.13 on 2024-05-12 09:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0022_userprofile_theme'),
]
operations = [
migrations.AlterField(
model_name='userprofile',
name='language',
field=models.CharField(blank=True, choices=[('en', 'English'), ('de', 'German'), ('es', 'Spanish'), ('zh-hans', 'Chinese Simplified'), ('ru', 'Russian'), ('ko', 'Korean'), ('fr', 'French'), ('ja', 'Japanese'), ('it', 'Italian'), ('uk', 'Ukrainian'), ('pl', 'Polish')], default='', max_length=10, verbose_name='Language'),
),
]

View File

@@ -78,6 +78,7 @@ class UserProfile(models.Model):
JAPANESE = 'ja', _('Japanese') JAPANESE = 'ja', _('Japanese')
ITALIAN = 'it', _('Italian') ITALIAN = 'it', _('Italian')
UKRAINIAN = 'uk', _('Ukrainian') UKRAINIAN = 'uk', _('Ukrainian')
POLISH = 'pl', _("Polish")
user = models.OneToOneField( user = models.OneToOneField(
User, User,

View File

@@ -16,7 +16,7 @@ class _TaskCounts(NamedTuple):
retried: int retried: int
failed: int failed: int
total: int total: int
earliest_task: Optional[dt.datetime] earliest_task: dt.datetime | None
hours: int hours: int

View File

@@ -17,7 +17,7 @@ class EventSeries:
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES" _ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
def __init__(self, key_id: str, redis: Optional[Redis] = None) -> None: def __init__(self, key_id: str, redis: Redis | None = None) -> None:
self._redis = get_redis_client_or_stub() if not redis else redis self._redis = get_redis_client_or_stub() if not redis else redis
self._key_id = str(key_id) self._key_id = str(key_id)
self.clear() self.clear()
@@ -46,7 +46,7 @@ class EventSeries:
my_id = self._redis.incr(self._key_counter) my_id = self._redis.incr(self._key_counter)
self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()}) self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()})
def all(self) -> List[dt.datetime]: def all(self) -> list[dt.datetime]:
"""List of all known events.""" """List of all known events."""
return [ return [
event[1] event[1]
@@ -75,7 +75,7 @@ class EventSeries:
maximum = "+inf" if not latest else latest.timestamp() maximum = "+inf" if not latest else latest.timestamp()
return self._redis.zcount(self._key_sorted_set, min=minimum, max=maximum) return self._redis.zcount(self._key_sorted_set, min=minimum, max=maximum)
def first_event(self, earliest: dt.datetime = None) -> Optional[dt.datetime]: def first_event(self, earliest: dt.datetime = None) -> dt.datetime | None:
"""Date/Time of first event. Returns `None` if series has no events. """Date/Time of first event. Returns `None` if series has no events.
Args: Args:

View File

@@ -7,7 +7,7 @@
{% translate "Dashboard" %} {% translate "Dashboard" %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="d-flex justify-content-around align-self-center flex-wrap"> <div class="row">
{% for dash in views %} {% for dash in views %}
{{ dash | safe }} {{ dash | safe }}
{% endfor %} {% endfor %}

View File

@@ -1,13 +1,11 @@
{% load i18n %} {% load i18n %}
<div id="aa-dashboard-panel-characters" class="col-12 col-xl-8 align-self-stretch p-2 ps-0 pe-0 ps-xl-0 pe-xl-2"> <div id="aa-dashboard-panel-characters" class="col-12 col-xl-8 mb-3">
<div class="card"> <div class="card h-100">
<div class="card-body"> <div class="card-body">
<div class="d-flex align-items-center"> {% translate "Characters" as widget_title %}
<h4 class="ms-auto me-auto"> {% include "framework/dashboard/widget-title.html" with title=widget_title %}
{% translate "Characters" %}
</h4> <div>
</div>
<div class="card-body">
<div style="height: 300px; overflow-y:auto;"> <div style="height: 300px; overflow-y:auto;">
<div class="d-flex"> <div class="d-flex">
<a href="{% url 'authentication:add_character' %}" class="btn btn-primary flex-fill m-1" title="{% translate 'Add Character' %}"> <a href="{% url 'authentication:add_character' %}" class="btn btn-primary flex-fill m-1" title="{% translate 'Add Character' %}">

View File

@@ -1,9 +1,11 @@
{% load i18n %} {% load i18n %}
<div id="aa-dashboard-panel-membership" class="col-12 col-xl-4 align-self-stretch py-2 ps-xl-2"> <div id="aa-dashboard-panel-membership" class="col-12 col-xl-4 mb-3">
<div class="card h-100"> <div class="card h-100">
<div class="card-body"> <div class="card-body">
<h4 class="card-title text-center">{% translate "Membership" %}</h4> {% translate "Membership" as widget_title %}
<div class="card-body"> {% include "framework/dashboard/widget-title.html" with title=widget_title %}
<div>
<div style="height: 300px; overflow-y:auto;"> <div style="height: 300px; overflow-y:auto;">
<h5 class="text-center">{% translate "State:" %} {{ request.user.profile.state }}</h5> <h5 class="text-center">{% translate "State:" %} {{ request.user.profile.state }}</h5>
<table class="table"> <table class="table">

186
allianceauth/checks.py Normal file
View File

@@ -0,0 +1,186 @@
from django import db
from django.core.checks import CheckMessage, Error, register, Warning
from allianceauth.utils.cache import get_redis_client
from django.utils import timezone
from packaging.version import InvalidVersion, Version as Pep440Version
from celery import current_app
from django.conf import settings
from sqlite3.dbapi2 import sqlite_version_info
import datetime
"""
A = System Packages
B = Configuration
"""
@register()
def django_settings(app_configs, **kwargs) -> list[CheckMessage]:
errors: list[CheckMessage] = []
if hasattr(settings, "SITE_URL"):
if settings.SITE_URL[-1] == "/":
errors.append(Warning("'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth.", hint="", id="allianceauth.checks.B005"))
else:
errors.append(Error("No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.", hint="", id="allianceauth.checks.B006"))
if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"):
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS:
errors.append(Warning("'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.", hint="", id="allianceauth.checks.B007"))
else:
errors.append(Error("No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified", hint="", id="allianceauth.checks.B008"))
return errors
@register()
def system_package_redis(app_configs, **kwargs) -> list[CheckMessage]:
errors: list[CheckMessage] = []
try:
redis_version = Pep440Version(get_redis_client().info()['redis_version'])
except InvalidVersion:
errors.append(Warning("Unable to confirm Redis Version"))
return errors
if redis_version.major == 7 and redis_version.minor == 2 and timezone.now() > timezone.datetime(year=2025, month=8, day=31, tzinfo=datetime.timezone.utc):
errors.append(Error(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A001"))
elif redis_version.major == 7 and redis_version.minor == 0:
errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A002"))
elif redis_version.major == 6 and redis_version.minor == 2:
errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A018"))
elif redis_version.major in [6, 5]:
errors.append(Error(f"Redis {redis_version.public} EOL", hint="https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A003"))
return errors
@register()
def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]:
errors: list[CheckMessage] = []
for connection in db.connections.all():
if connection.vendor == "mysql":
try:
mysql_version = Pep440Version(".".join(str(i) for i in connection.mysql_version))
except InvalidVersion:
errors.append(Warning("Unable to confirm MySQL Version"))
return errors
# MySQL 8
if mysql_version.major == 8 and mysql_version.minor == 4 and timezone.now() > timezone.datetime(year=2032, month=4, day=30, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A004"))
elif mysql_version.major == 8 and mysql_version.minor == 3:
errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A005"))
elif mysql_version.major == 8 and mysql_version.minor == 2:
errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A006"))
elif mysql_version.major == 8 and mysql_version.minor == 1:
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A007"))
elif mysql_version.major == 8 and mysql_version.minor == 0 and timezone.now() > timezone.datetime(year=2026, month=4, day=30, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A008"))
elif mysql_version.major < 8: # This will also catch Mariadb 5.x
errors.append(Error(f"MySQL or MariaDB {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A009"))
return errors
@register()
def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]:
errors: list[CheckMessage] = []
for connection in db.connections.all():
if connection.vendor == "mysql": # Still to find a way to determine MySQL vs MariaDB
try:
mariadb_version = Pep440Version(".".join(str(i) for i in connection.mysql_version))
except InvalidVersion:
errors.append(Warning("Unable to confirm MariaDB Version"))
return errors
# MariaDB 11
if mariadb_version.major == 11 and mariadb_version.minor == 4 and timezone.now() > timezone.datetime(year=2029, month=5, day=19, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A010"))
elif mariadb_version.major == 11 and mariadb_version.minor == 2:
errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A018"))
if timezone.now() > timezone.datetime(year=2024, month=11, day=21, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A011"))
elif mariadb_version.major == 11 and mariadb_version.minor == 1:
errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A019"))
if timezone.now() > timezone.datetime(year=2024, month=8, day=21, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A012"))
elif mariadb_version.major == 11 and mariadb_version.minor in [0, 3]: # Demote versions down here once EOL
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config.", id="allianceauth.checks.A013"))
# MariaDB 10
elif mariadb_version.major == 10 and mariadb_version.minor == 11 and timezone.now() > timezone.datetime(year=2028, month=2, day=10, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config.", id="allianceauth.checks.A014"))
elif mariadb_version.major == 10 and mariadb_version.minor == 6 and timezone.now() > timezone.datetime(year=2026, month=7, day=6, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A0015"))
elif mariadb_version.major == 10 and mariadb_version.minor == 5 and timezone.now() > timezone.datetime(year=2025, month=6, day=24, tzinfo=datetime.timezone.utc):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A016"))
elif mariadb_version.major == 10 and mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]: # Demote versions down here once EOL
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A017"))
return errors
@register()
def system_package_sqlite(app_configs, **kwargs) -> list[CheckMessage]:
errors: list[CheckMessage] = []
for connection in db.connections.all():
if connection.vendor == "sqlite":
try:
sqlite_version = Pep440Version(".".join(str(i) for i in sqlite_version_info))
except InvalidVersion:
errors.append(Warning("Unable to confirm SQLite Version"))
return errors
if sqlite_version.major == 3 and sqlite_version.minor < 27:
errors.append(Error(f"SQLite {sqlite_version.public} Unsupported by Django", hint="https://pkgs.org/download/sqlite3", id="allianceauth.checks.A020"))
return errors
@register()
def sql_settings(app_configs, **kwargs) -> list[CheckMessage]:
errors: list[CheckMessage] = []
for connection in db.connections.all():
if connection.vendor == "mysql":
try:
if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4":
errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001"))
except KeyError:
errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001"))
# This hasn't actually been set on AA yet
# try:
# if connection.settings_dict["OPTIONS"]["collation"] != "utf8mb4_unicode_ci":
# errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001"))
# except KeyError:
# errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001"))
# if connection.vendor == "sqlite":
return errors
@register()
def celery_settings(app_configs, **kwargs) -> list[CheckMessage]:
errors: list[CheckMessage] = []
try:
if current_app.conf.broker_transport_options != {'priority_steps': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'queue_order_strategy': 'priority'}:
errors.append(Error("Celery Priorities are not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003"))
except KeyError:
errors.append(Error("Celery Priorities are not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003"))
try:
if current_app.conf.broker_connection_retry_on_startup != True:
errors.append(Error("Celery broker_connection_retry_on_startup not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004"))
except KeyError:
errors.append(Error("Celery broker_connection_retry_on_startup not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004"))
return errors
# IDEAS
# Any other celery things weve manually changed over the years
# I'd be happy to add Community App checks, old versions the owners dont want to support etc.
# Check Default Collation on DB
# Check Charset Collation on all tables

View File

@@ -235,7 +235,7 @@ class EveCharacter(models.Model):
return self.corporation_id == DOOMHEIM_CORPORATION_ID return self.corporation_id == DOOMHEIM_CORPORATION_ID
@property @property
def alliance(self) -> Union[EveAllianceInfo, None]: def alliance(self) -> EveAllianceInfo | None:
""" """
Pseudo foreign key from alliance_id to EveAllianceInfo Pseudo foreign key from alliance_id to EveAllianceInfo
:raises: EveAllianceInfo.DoesNotExist :raises: EveAllianceInfo.DoesNotExist
@@ -255,7 +255,7 @@ class EveCharacter(models.Model):
return EveCorporationInfo.objects.get(corporation_id=self.corporation_id) return EveCorporationInfo.objects.get(corporation_id=self.corporation_id)
@property @property
def faction(self) -> Union[EveFactionInfo, None]: def faction(self) -> EveFactionInfo | None:
""" """
Pseudo foreign key from faction_id to EveFactionInfo Pseudo foreign key from faction_id to EveFactionInfo
:raises: EveFactionInfo.DoesNotExist :raises: EveFactionInfo.DoesNotExist

View File

@@ -13,7 +13,7 @@ from allianceauth.framework.api.user import get_sentinel_user
def get_main_character_from_evecharacter( def get_main_character_from_evecharacter(
character: EveCharacter, character: EveCharacter,
) -> Optional[EveCharacter]: ) -> EveCharacter | None:
""" """
Get the main character for a given EveCharacter or None when no main character is set Get the main character for a given EveCharacter or None when no main character is set

View File

@@ -34,7 +34,7 @@ def get_all_characters_from_user(user: User) -> list:
return characters return characters
def get_main_character_from_user(user: User) -> Optional[EveCharacter]: def get_main_character_from_user(user: User) -> EveCharacter | None:
""" """
Get the main character from a user Get the main character from a user

View File

@@ -0,0 +1,8 @@
{#Usage:#}
{# {% include "framework/dashboard/widget-title.html" with title="Foobar" %}#}
<div class="text-center">
<h4 class="ms-auto me-auto mb-3">
{{ title }}
</h4>
</div>

View File

@@ -7,7 +7,7 @@
{% if subtitle %} {% if subtitle %}
<br> <br>
<small>{{ subtitle }}</small> <small class="text-muted">{{ subtitle }}</small>
{% endif %} {% endif %}
</h1> </h1>
{% endif %} {% endif %}

View File

@@ -181,7 +181,7 @@ class AuthGroup(models.Model):
def __str__(self): def __str__(self):
return self.group.name return self.group.name
def group_request_approvers(self) -> Set[User]: def group_request_approvers(self) -> set[User]:
"""Return all users who can approve a group request.""" """Return all users who can approve a group request."""
return set( return set(
self.group_leaders.all() self.group_leaders.all()

View File

@@ -5,7 +5,7 @@ from typing import Optional
class ApplicationManager(models.Manager): class ApplicationManager(models.Manager):
def pending_requests_count_for_user(self, user: User) -> Optional[int]: def pending_requests_count_for_user(self, user: User) -> int | None:
"""Returns the number of pending group requests for the given user""" """Returns the number of pending group requests for the given user"""
if user.is_superuser: if user.is_superuser:
return self.filter(approved__isnull=True).count() return self.filter(approved__isnull=True).count()

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Peter Pfeufer, 2024\n" "Last-Translator: Peter Pfeufer, 2024\n"
"Language-Team: German (https://app.transifex.com/alliance-auth/teams/107430/de/)\n" "Language-Team: German (https://app.transifex.com/alliance-auth/teams/107430/de/)\n"
@@ -1449,6 +1449,8 @@ msgid "Sign Out"
msgstr "Ausloggen" msgstr "Ausloggen"
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "Einloggen" msgstr "Einloggen"

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1385,6 +1385,8 @@ msgid "Sign Out"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "" msgstr ""

View File

@@ -15,7 +15,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: trenus, 2023\n" "Last-Translator: trenus, 2023\n"
"Language-Team: Spanish (https://app.transifex.com/alliance-auth/teams/107430/es/)\n" "Language-Team: Spanish (https://app.transifex.com/alliance-auth/teams/107430/es/)\n"
@@ -1439,6 +1439,8 @@ msgid "Sign Out"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "" msgstr ""

View File

@@ -2,7 +2,7 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Mickael Gr4vity, 2023 # Mickael Gr4vity, 2023
# rockclodbuster, 2023 # rockclodbuster, 2023
@@ -12,15 +12,16 @@
# draktanar KarazGrong <umbre@fallenstarscreations.com>, 2023 # draktanar KarazGrong <umbre@fallenstarscreations.com>, 2023
# Geoffrey Fabbro, 2023 # Geoffrey Fabbro, 2023
# Idea, 2024 # Idea, 2024
# # Joel Falknau <ozirascal@gmail.com>, 2024
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Idea, 2024\n" "Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2024\n"
"Language-Team: French (France) (https://app.transifex.com/alliance-auth/teams/107430/fr_FR/)\n" "Language-Team: French (France) (https://app.transifex.com/alliance-auth/teams/107430/fr_FR/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -1458,6 +1459,8 @@ msgid "Sign Out"
msgstr "Se Déconnecter" msgstr "Se Déconnecter"
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "Se Connecter" msgstr "Se Connecter"

View File

@@ -6,16 +6,16 @@
# Translators: # Translators:
# Alessandro Cresti, 2023 # Alessandro Cresti, 2023
# Linus Hope, 2023 # Linus Hope, 2023
# Thomas Turini, 2024 # Tuz, 2024
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Thomas Turini, 2024\n" "Last-Translator: Tuz, 2024\n"
"Language-Team: Italian (Italy) (https://app.transifex.com/alliance-auth/teams/107430/it_IT/)\n" "Language-Team: Italian (Italy) (https://app.transifex.com/alliance-auth/teams/107430/it_IT/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -1456,6 +1456,8 @@ msgid "Sign Out"
msgstr "Sign Out" msgstr "Sign Out"
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "Sign In" msgstr "Sign In"

View File

@@ -13,7 +13,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: kotaneko, 2024\n" "Last-Translator: kotaneko, 2024\n"
"Language-Team: Japanese (https://app.transifex.com/alliance-auth/teams/107430/ja/)\n" "Language-Team: Japanese (https://app.transifex.com/alliance-auth/teams/107430/ja/)\n"
@@ -1405,6 +1405,8 @@ msgid "Sign Out"
msgstr "サインアウト" msgstr "サインアウト"
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "サインイン" msgstr "サインイン"

View File

@@ -18,7 +18,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Woojin Kang, 2024\n" "Last-Translator: Woojin Kang, 2024\n"
"Language-Team: Korean (Korea) (https://app.transifex.com/alliance-auth/teams/107430/ko_KR/)\n" "Language-Team: Korean (Korea) (https://app.transifex.com/alliance-auth/teams/107430/ko_KR/)\n"
@@ -1414,6 +1414,8 @@ msgid "Sign Out"
msgstr "탈퇴" msgstr "탈퇴"
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "가입" msgstr "가입"

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -2,22 +2,23 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Андрей Зубков <and.vareba81@gmail.com>, 2023 # Андрей Зубков <and.vareba81@gmail.com>, 2023
# Yuriy K <thedjcooltv@gmail.com>, 2023 # Yuriy K <thedjcooltv@gmail.com>, 2023
# Alexander Gess <de.alex.gess@gmail.com>, 2023 # Alexander Gess <de.alex.gess@gmail.com>, 2023
# Filipp Chertiev <f@fzfx.ru>, 2023 # Filipp Chertiev <f@fzfx.ru>, 2023
# Ruslan Virchich, 2024 # Ruslan Virchich, 2024
# # Joel Falknau <ozirascal@gmail.com>, 2024
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Ruslan Virchich, 2024\n" "Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2024\n"
"Language-Team: Russian (https://app.transifex.com/alliance-auth/teams/107430/ru/)\n" "Language-Team: Russian (https://app.transifex.com/alliance-auth/teams/107430/ru/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -1435,6 +1436,8 @@ msgid "Sign Out"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "" msgstr ""

View File

@@ -13,7 +13,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Andrii Yukhymchak, 2024\n" "Last-Translator: Andrii Yukhymchak, 2024\n"
"Language-Team: Ukrainian (https://app.transifex.com/alliance-auth/teams/107430/uk/)\n" "Language-Team: Ukrainian (https://app.transifex.com/alliance-auth/teams/107430/uk/)\n"
@@ -1454,6 +1454,8 @@ msgid "Sign Out"
msgstr "Вийти" msgstr "Вийти"
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "Увійти" msgstr "Увійти"

View File

@@ -14,7 +14,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-13 19:10+1000\n" "POT-Creation-Date: 2024-05-12 19:15+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2023\n" "Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2023\n"
"Language-Team: Chinese Simplified (https://app.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n" "Language-Team: Chinese Simplified (https://app.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n"
@@ -1391,6 +1391,8 @@ msgid "Sign Out"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:84 #: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "" msgstr ""

View File

@@ -35,12 +35,12 @@ class MenuItemAdmin(admin.ModelAdmin):
] ]
ordering = ["parent", "order", "text"] ordering = ["parent", "order", "text"]
def get_form(self, request: HttpRequest, obj: Optional[MenuItem] = None, **kwargs): def get_form(self, request: HttpRequest, obj: MenuItem | None = None, **kwargs):
kwargs["form"] = self._choose_form(request, obj) kwargs["form"] = self._choose_form(request, obj)
return super().get_form(request, obj, **kwargs) return super().get_form(request, obj, **kwargs)
@classmethod @classmethod
def _choose_form(cls, request: HttpRequest, obj: Optional[MenuItem]): def _choose_form(cls, request: HttpRequest, obj: MenuItem | None):
"""Return the form for the current menu item type.""" """Return the form for the current menu item type."""
if obj: # change if obj: # change
if obj.hook_hash: if obj.hook_hash:
@@ -104,7 +104,7 @@ class MenuItemAdmin(admin.ModelAdmin):
@staticmethod @staticmethod
def _type_from_request( def _type_from_request(
request: HttpRequest, default=None request: HttpRequest, default=None
) -> Optional[MenuItemType]: ) -> MenuItemType | None:
try: try:
return MenuItemType(request.GET.get("type")) return MenuItemType(request.GET.get("type"))
except ValueError: except ValueError:

View File

@@ -14,8 +14,8 @@ class MenuItemHookCustom(MenuItemHook):
text: str, text: str,
classes: str, classes: str,
url_name: str, url_name: str,
order: Optional[int] = None, order: int | None = None,
navactive: Optional[List[str]] = None, navactive: list[str] | None = None,
): ):
super().__init__(text, classes, url_name, order, navactive) super().__init__(text, classes, url_name, order, navactive)
self.url = "" self.url = ""

View File

@@ -33,8 +33,8 @@ class MenuItemHook:
text: str, text: str,
classes: str, classes: str,
url_name: str, url_name: str,
order: Optional[int] = None, order: int | None = None,
navactive: Optional[List[str]] = None, navactive: list[str] | None = None,
): ):
self.text = text self.text = text
self.classes = classes self.classes = classes

View File

@@ -60,15 +60,17 @@
<li> <li>
<a class="dropdown-item" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i> Alliance Auth Discord</a> <a class="dropdown-item" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i> Alliance Auth Discord</a>
</li> </li>
<li>
<a class="dropdown-item" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i> Alliance Auth Git</a>
</li>
<li> <li>
<a class="dropdown-item" href="{% url 'admin:index' %}"> <a class="dropdown-item" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i> Alliance Auth Git</a>
<i class="fa-solid fa-gear fa-fw"></i> {% translate "Admin" %}
</a>
</li> </li>
{% endif %} {% endif %}
{% if user.is_staff %}
<li>
<a class="dropdown-item" href="{% url 'admin:index' %}">
<i class="fa-solid fa-gear fa-fw"></i> {% translate "Admin" %}
</a>
</li>
{% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li> <li>

View File

@@ -56,8 +56,8 @@ class RenderedMenuItem:
menu_item: MenuItem menu_item: MenuItem
children: List["RenderedMenuItem"] = field(default_factory=list) children: list["RenderedMenuItem"] = field(default_factory=list)
count: Optional[int] = None count: int | None = None
html: str = "" html: str = ""
html_id: str = "" html_id: str = ""
@@ -78,7 +78,7 @@ class RenderedMenuItem:
self.html_id = hook_obj.html_id self.html_id = hook_obj.html_id
def render_menu(request: HttpRequest) -> List[RenderedMenuItem]: def render_menu(request: HttpRequest) -> list[RenderedMenuItem]:
"""Return the rendered side menu for including in a template. """Return the rendered side menu for including in a template.
This function is creating BS5 style menus. This function is creating BS5 style menus.
@@ -88,7 +88,7 @@ def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
# Menu items needs to be rendered with the new BS5 template # Menu items needs to be rendered with the new BS5 template
bs5_template = "menu/menu-item-bs5.html" bs5_template = "menu/menu-item-bs5.html"
rendered_items: Dict[int, RenderedMenuItem] = {} rendered_items: dict[int, RenderedMenuItem] = {}
menu_items: QuerySet[MenuItem] = MenuItem.objects.order_by( menu_items: QuerySet[MenuItem] = MenuItem.objects.order_by(
"parent", "order", "text" "parent", "order", "text"
) )
@@ -131,7 +131,7 @@ def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
return list(rendered_items.values()) return list(rendered_items.values())
def _gather_menu_items_from_hooks() -> Dict[str, MenuItemHook]: def _gather_menu_items_from_hooks() -> dict[str, MenuItemHook]:
hook_items = {} hook_items = {}
for hook in get_hooks("menu_item_hook"): for hook in get_hooks("menu_item_hook"):
f = hook() f = hook()
@@ -161,14 +161,14 @@ def _render_link_item(
def _render_folder_items( def _render_folder_items(
request: HttpRequest, rendered_items: Dict[int, RenderedMenuItem], new_template: str request: HttpRequest, rendered_items: dict[int, RenderedMenuItem], new_template: str
): ):
for item in rendered_items.values(): for item in rendered_items.values():
if item.menu_item.is_folder: if item.menu_item.is_folder:
item.update_html(request=request, template=new_template) item.update_html(request=request, template=new_template)
def _remove_empty_folders(rendered_items: Dict[int, RenderedMenuItem]): def _remove_empty_folders(rendered_items: dict[int, RenderedMenuItem]):
ids_to_remove = [] ids_to_remove = []
for item_id, item in rendered_items.items(): for item_id, item in rendered_items.items():
if item.is_folder and not item.children: if item.is_folder and not item.children:

View File

@@ -347,9 +347,9 @@ class TestRenderedMenuItem(TestCase):
class _ParsedMenuItem(NamedTuple): class _ParsedMenuItem(NamedTuple):
classes: List[str] classes: list[str]
text: str text: str
count: Optional[int] count: int | None
def parse_html(obj: RenderedMenuItem) -> _ParsedMenuItem: def parse_html(obj: RenderedMenuItem) -> _ParsedMenuItem:

View File

@@ -1,41 +1,40 @@
{% load i18n %} {% load i18n %}
{% load evelinks %} {% load evelinks %}
<div class="col-12 align-self-stretch py-2"> <div class="col-12 mb-3">
<div class="card h-100"> <div class="card h-100">
<div class="card-body"> <div class="card-body">
<h4 class="card-title text-center">{% translate "Upcoming Fleets" %}</h4> {% translate "Upcoming Fleets" as widget_title %}
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
<div class="card-body"> <div>
<div> <table class="table">
<table class="table"> <thead>
<thead> <tr>
<th class="text-center">{% translate "Operation" %}</th>
<th class="text-center">{% translate "Type" %}</th>
<th class="text-center">{% translate "Form Up System" %}</th>
<th class="text-center">{% translate "EVE Time" %}</th>
</tr>
</thead>
<tbody>
{% for ops in timers %}
<tr> <tr>
<th class="text-center">{% translate "Operation" %}</th> <td class="text-center">
<th class="text-center">{% translate "Type" %}</th> {{ ops.operation_name }}
<th class="text-center">{% translate "Form Up System" %}</th> </td>
<th class="text-center">{% translate "EVE Time" %}</th> <td class="text-center">
{{ ops.type }}
</td>
<td class="text-center">
<a href="{{ ops.system|dotlan_solar_system_url }}">{{ ops.system }}</a>
</td>
<td class="text-center" nowrap>{{ ops.start | date:"Y-m-d H:i" }}</td>
</tr> </tr>
</thead> {% endfor %}
</tbody>
<tbody> </table>
{% for ops in timers %}
<tr>
<td class="text-center">
{{ ops.operation_name }}
</td>
<td class="text-center">
{{ ops.type }}
</td>
<td class="text-center">
<a href="{{ ops.system|dotlan_solar_system_url }}">{{ ops.system }}</a>
</td>
<td class="text-center" nowrap>{{ ops.start | date:"Y-m-d H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -102,6 +102,7 @@ LANGUAGES = (
("ja", "Japanese"), ("ja", "Japanese"),
("it", "Italian"), ("it", "Italian"),
("uk", "Ukrainian"), ("uk", "Ukrainian"),
("pl", "Polish"),
) )
TEMPLATES = [ TEMPLATES = [

View File

@@ -1,6 +1,7 @@
from string import Formatter from string import Formatter
from django.urls import include, re_path from django.urls import include, re_path
from typing import Iterable, Optional from typing import Optional
from collections.abc import Iterable
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@@ -175,7 +176,7 @@ class UrlHook:
urls, urls,
namespace: str, namespace: str,
base_url: str, base_url: str,
excluded_views : Optional[Iterable[str]] = None excluded_views : Iterable[str] | None = None
): ):
self.include_pattern = re_path(base_url, include(urls, namespace=namespace)) self.include_pattern = re_path(base_url, include(urls, namespace=namespace))
self.excluded_views = set(excluded_views or []) self.excluded_views = set(excluded_views or [])

View File

@@ -28,7 +28,7 @@ from .models import DiscordUser # noqa
__all__ = ["create_bot_client", "group_to_role", "server_name", "DiscordUser", "Role"] __all__ = ["create_bot_client", "group_to_role", "server_name", "DiscordUser", "Role"]
def discord_guild_id() -> Optional[int]: def discord_guild_id() -> int | None:
"""Guild ID of configured Discord server. """Guild ID of configured Discord server.
Returns: Returns:

View File

@@ -36,7 +36,7 @@ def calculate_roles_for_user(
client: DiscordClient, client: DiscordClient,
discord_uid: int, discord_uid: int,
state_name: str = None, state_name: str = None,
) -> Tuple[RolesSet, Optional[bool]]: ) -> tuple[RolesSet, bool | None]:
"""Calculate current Discord roles for an Auth user. """Calculate current Discord roles for an Auth user.
Takes into account reserved groups and existing managed roles (e.g. nitro). Takes into account reserved groups and existing managed roles (e.g. nitro).
@@ -68,7 +68,7 @@ def calculate_roles_for_user(
return roles_calculated.union(roles_persistent), True return roles_calculated.union(roles_persistent), True
def _user_group_names(user: User, state_name: str = None) -> List[str]: def _user_group_names(user: User, state_name: str = None) -> list[str]:
"""Names of groups and state the given user is a member of.""" """Names of groups and state the given user is a member of."""
if not state_name: if not state_name:
state_name = user.profile.state.name state_name = user.profile.state.name
@@ -77,7 +77,7 @@ def _user_group_names(user: User, state_name: str = None) -> List[str]:
return group_names return group_names
def user_formatted_nick(user: User) -> Optional[str]: def user_formatted_nick(user: User) -> str | None:
"""Name of the given user's main character with name formatting applied. """Name of the given user's main character with name formatting applied.
Returns: Returns:
@@ -90,7 +90,7 @@ def user_formatted_nick(user: User) -> Optional[str]:
return None return None
def group_to_role(group: Group) -> Optional[Role]: def group_to_role(group: Group) -> Role | None:
"""Fetch the Discord role matching the given Django group by name. """Fetch the Discord role matching the given Django group by name.
Returns: Returns:

View File

@@ -6,7 +6,8 @@ from enum import IntEnum
from hashlib import md5 from hashlib import md5
from http import HTTPStatus from http import HTTPStatus
from time import sleep from time import sleep
from typing import Iterable, List, Optional, Set, Tuple from typing import List, Optional, Set, Tuple
from collections.abc import Iterable
from urllib.parse import urljoin from urllib.parse import urljoin
from uuid import uuid1 from uuid import uuid1
@@ -233,7 +234,7 @@ class DiscordClient:
# guild roles # guild roles
def guild_roles(self, guild_id: int, use_cache: bool = True) -> Set[Role]: def guild_roles(self, guild_id: int, use_cache: bool = True) -> set[Role]:
"""Fetch all roles for this guild. """Fetch all roles for this guild.
Args: Args:
@@ -268,7 +269,7 @@ class DiscordClient:
def create_guild_role( def create_guild_role(
self, guild_id: int, role_name: str, **kwargs self, guild_id: int, role_name: str, **kwargs
) -> Optional[Role]: ) -> Role | None:
"""Create a new guild role with the given name. """Create a new guild role with the given name.
See official documentation for additional optional parameters. See official documentation for additional optional parameters.
@@ -318,7 +319,7 @@ class DiscordClient:
gen_key = cls._generate_hash(f'{guild_id}') gen_key = cls._generate_hash(f'{guild_id}')
return f'{cls._KEYPREFIX_GUILD_ROLES}__{gen_key}' return f'{cls._KEYPREFIX_GUILD_ROLES}__{gen_key}'
def match_role_from_name(self, guild_id: int, role_name: str) -> Optional[Role]: def match_role_from_name(self, guild_id: int, role_name: str) -> Role | None:
"""Fetch Discord role matching the given name (cached). """Fetch Discord role matching the given name (cached).
Args: Args:
@@ -333,7 +334,7 @@ class DiscordClient:
def match_or_create_roles_from_names( def match_or_create_roles_from_names(
self, guild_id: int, role_names: Iterable[str] self, guild_id: int, role_names: Iterable[str]
) -> List[Tuple[Role, bool]]: ) -> list[tuple[Role, bool]]:
"""Fetch or create Discord roles matching the given names (cached). """Fetch or create Discord roles matching the given names (cached).
Will try to match with existing roles names Will try to match with existing roles names
@@ -361,7 +362,7 @@ class DiscordClient:
def match_or_create_role_from_name( def match_or_create_role_from_name(
self, guild_id: int, role_name: str, guild_roles: RolesSet = None self, guild_id: int, role_name: str, guild_roles: RolesSet = None
) -> Tuple[Role, bool]: ) -> tuple[Role, bool]:
"""Fetch or create Discord role matching the given name. """Fetch or create Discord role matching the given name.
Will try to match with existing roles names Will try to match with existing roles names
@@ -418,7 +419,7 @@ class DiscordClient:
access_token: str, access_token: str,
role_ids: list = None, role_ids: list = None,
nick: str = None nick: str = None
) -> Optional[bool]: ) -> bool | None:
"""Adds a user to the guild. """Adds a user to the guild.
Returns: Returns:
@@ -442,7 +443,7 @@ class DiscordClient:
return None return None
return False return False
def guild_member(self, guild_id: int, user_id: int) -> Optional[GuildMember]: def guild_member(self, guild_id: int, user_id: int) -> GuildMember | None:
"""Fetch info for a guild member. """Fetch info for a guild member.
Args: Args:
@@ -461,8 +462,8 @@ class DiscordClient:
return GuildMember.from_dict(r.json()) return GuildMember.from_dict(r.json())
def modify_guild_member( def modify_guild_member(
self, guild_id: int, user_id: int, role_ids: List[int] = None, nick: str = None self, guild_id: int, user_id: int, role_ids: list[int] = None, nick: str = None
) -> Optional[bool]: ) -> bool | None:
"""Set properties of a guild member. """Set properties of a guild member.
Args: Args:
@@ -501,7 +502,7 @@ class DiscordClient:
return True return True
return False return False
def remove_guild_member(self, guild_id: int, user_id: int) -> Optional[bool]: def remove_guild_member(self, guild_id: int, user_id: int) -> bool | None:
"""Remove a member from a guild. """Remove a member from a guild.
Args: Args:
@@ -529,7 +530,7 @@ class DiscordClient:
def add_guild_member_role( def add_guild_member_role(
self, guild_id: int, user_id: int, role_id: int self, guild_id: int, user_id: int, role_id: int
) -> Optional[bool]: ) -> bool | None:
"""Adds a role to a guild member """Adds a role to a guild member
Returns: Returns:
@@ -549,7 +550,7 @@ class DiscordClient:
def remove_guild_member_role( def remove_guild_member_role(
self, guild_id: int, user_id: int, role_id: int self, guild_id: int, user_id: int, role_id: int
) -> Optional[bool]: ) -> bool | None:
"""Remove a role to a guild member """Remove a role to a guild member
Args: Args:
@@ -572,7 +573,7 @@ class DiscordClient:
return True return True
return False return False
def guild_member_roles(self, guild_id: int, user_id: int) -> Optional[RolesSet]: def guild_member_roles(self, guild_id: int, user_id: int) -> RolesSet | None:
"""Fetch the current guild roles of a guild member. """Fetch the current guild roles of a guild member.
Args: Args:
@@ -821,6 +822,6 @@ class DiscordClient:
return md5(key.encode('utf-8')).hexdigest() return md5(key.encode('utf-8')).hexdigest()
@staticmethod @staticmethod
def _sanitize_role_ids(role_ids: Iterable[int]) -> List[int]: def _sanitize_role_ids(role_ids: Iterable[int]) -> list[int]:
"""Sanitize a list of role IDs, i.e. make sure its a list of unique integers.""" """Sanitize a list of role IDs, i.e. make sure its a list of unique integers."""
return [int(role_id) for role_id in set(role_ids)] return [int(role_id) for role_id in set(role_ids)]

View File

@@ -1,5 +1,6 @@
from copy import copy from copy import copy
from typing import Iterable, List, Optional, Set, Tuple from typing import List, Optional, Set, Tuple
from collections.abc import Iterable
from .models import Role from .models import Role
@@ -50,7 +51,7 @@ class RolesSet:
def __len__(self): def __len__(self):
return len(self._roles.keys()) return len(self._roles.keys())
def has_roles(self, role_ids: Set[int]) -> bool: def has_roles(self, role_ids: set[int]) -> bool:
"""True if this objects contains all roles defined by given role_ids """True if this objects contains all roles defined by given role_ids
incl. managed roles. incl. managed roles.
""" """
@@ -58,7 +59,7 @@ class RolesSet:
all_role_ids = self._roles.keys() all_role_ids = self._roles.keys()
return role_ids.issubset(all_role_ids) return role_ids.issubset(all_role_ids)
def ids(self) -> Set[int]: def ids(self) -> set[int]:
"""Set of all role IDs.""" """Set of all role IDs."""
return set(self._roles.keys()) return set(self._roles.keys())
@@ -114,7 +115,7 @@ class RolesSet:
new_ids = self.ids().difference(other.ids()) new_ids = self.ids().difference(other.ids())
return self.subset(role_ids=new_ids) return self.subset(role_ids=new_ids)
def role_by_name(self, role_name: str) -> Optional[Role]: def role_by_name(self, role_name: str) -> Role | None:
"""Role if one with matching name is found else None.""" """Role if one with matching name is found else None."""
role_name = Role.sanitize_name(role_name) role_name = Role.sanitize_name(role_name)
if role_name in self._roles_by_name: if role_name in self._roles_by_name:
@@ -123,7 +124,7 @@ class RolesSet:
@classmethod @classmethod
def create_from_matched_roles( def create_from_matched_roles(
cls, matched_roles: List[Tuple[Role, bool]] cls, matched_roles: list[tuple[Role, bool]]
) -> "RolesSet": ) -> "RolesSet":
"""Create new instance from the given list of matches roles. """Create new instance from the given list of matches roles.

View File

@@ -69,7 +69,7 @@ class Guild:
id: int id: int
name: str name: str
roles: FrozenSet[Role] roles: frozenset[Role]
def __post_init__(self): def __post_init__(self):
object.__setattr__(self, "id", int(self.id)) object.__setattr__(self, "id", int(self.id))
@@ -95,7 +95,7 @@ class GuildMember:
_NICK_MAX_CHARS = 32 _NICK_MAX_CHARS = 32
roles: FrozenSet[int] roles: frozenset[int]
nick: str = None nick: str = None
user: User = None user: User = None

View File

@@ -98,7 +98,7 @@ class DiscordUser(models.Model):
logger.warning('Failed to update nickname for %s', self.user) logger.warning('Failed to update nickname for %s', self.user)
return success return success
def update_groups(self, state_name: str = None) -> Optional[bool]: def update_groups(self, state_name: str = None) -> bool | None:
"""update groups for a user based on his current group memberships. """update groups for a user based on his current group memberships.
Will add or remove roles of a user as needed. Will add or remove roles of a user as needed.
@@ -134,7 +134,7 @@ class DiscordUser(models.Model):
logger.info('No need to update roles for user %s', self.user) logger.info('No need to update roles for user %s', self.user)
return True return True
def update_username(self) -> Optional[bool]: def update_username(self) -> bool | None:
"""Updates the username incl. the discriminator """Updates the username incl. the discriminator
from the Discord server and saves it from the Discord server and saves it
@@ -159,7 +159,7 @@ class DiscordUser(models.Model):
notify_user: bool = False, notify_user: bool = False,
is_rate_limited: bool = True, is_rate_limited: bool = True,
handle_api_exceptions: bool = False handle_api_exceptions: bool = False
) -> Optional[bool]: ) -> bool | None:
"""Deletes the Discount user both on the server and locally """Deletes the Discount user both on the server and locally
Params: Params:

View File

@@ -438,7 +438,7 @@ class TestUserHasAccount(NoSocketsTestCase):
self.assertFalse(DiscordUser.objects.user_has_account(self.user)) self.assertFalse(DiscordUser.objects.user_has_account(self.user))
def test_return_false_if_user_does_not_exist(self): def test_return_false_if_user_does_not_exist(self):
my_user = User(username='Dummy') my_user = AuthUtils.create_user("test_return_false_if_user_does_not_exist")
self.assertFalse(DiscordUser.objects.user_has_account(my_user)) self.assertFalse(DiscordUser.objects.user_has_account(my_user))
def test_return_false_if_not_called_with_user_object(self): def test_return_false_if_not_called_with_user_object(self):

View File

@@ -7,7 +7,9 @@
{% endblock %} {% endblock %}
{% block url %} {% block url %}
<a href="{{ service_url }}">{{ service_url }}</a> {% if username != '' %}
<a href="mumble://{{ connect_url }}">{{ service_url }}</a>
{% endif %}
{% endblock %} {% endblock %}
{% block user %} {% block user %}

View File

@@ -181,7 +181,7 @@ class SmfManager:
return out return out
@classmethod @classmethod
def add_user(cls, username, email_address, groups, main_character: EveCharacter) -> Tuple: def add_user(cls, username, email_address, groups, main_character: EveCharacter) -> tuple:
""" """
Add a user to SMF Add a user to SMF
:param username: :param username:

View File

@@ -1,5 +1,5 @@
{% load i18n %} {% load i18n %}
<div id="esi-alert" class="col-12 align-self-stretch py-2 collapse"> <div id="esi-alert" class="col-12 collapse">
<div class="alert alert-warning"> <div class="alert alert-warning">
<p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p> <p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p>
<hr> <hr>
@@ -23,7 +23,7 @@
console.log("ESI Check: ", JSON.stringify(responseJson, null, 2)); console.log("ESI Check: ", JSON.stringify(responseJson, null, 2));
const status = responseJson.status; const status = responseJson.status;
if (status != 200) { if (status !== 200) {
elemCode.textContent = status elemCode.textContent = status
elemMessage.textContent = responseJson.data.error; elemMessage.textContent = responseJson.data.error;
new bootstrap.Collapse(elemCard, { new bootstrap.Collapse(elemCard, {

View File

@@ -2,48 +2,43 @@
{% load humanize %} {% load humanize %}
{% if notifications %} {% if notifications %}
<div id="aa-dashboard-panel-admin-notifications" class="col-12 align-self-stretch pb-2"> <div id="aa-dashboard-panel-admin-notifications" class="col-12 mb-3">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="d-flex align-items-center"> {% translate "Alliance Auth Notifications" as widget_title %}
<div class="w-100 align-self-stretch"> {% include "framework/dashboard/widget-title.html" with title=widget_title %}
<h4 class="ms-auto me-auto text-center">
{% translate "Alliance Auth Notifications" %}
</h4>
<div class="card-body"> <div>
<ul class="list-group"> <ul class="list-group">
{% for notif in notifications %} {% for notif in notifications %}
<li class="list-group-item"> <li class="list-group-item">
{% if notif.state == 'opened' %} {% if notif.state == 'opened' %}
<span class="badge bg-success">{% translate "Open" %}</span> <span class="badge bg-success me-2">{% translate "Open" %}</span>
{% else %} {% else %}
<span class="badge bg-danger">{% translate "Closed" %}</span> <span class="badge bg-danger me-2">{% translate "Closed" %}</span>
{% endif %} {% endif %}
<a href="{{ notif.web_url }}" target="_blank">#{{ notif.iid }} {{ notif.title }}</a> <a href="{{ notif.web_url }}" target="_blank">#{{ notif.iid }} {{ notif.title }}</a>
</li> </li>
{% empty %} {% empty %}
<div class="alert alert-primary" role="alert"> <div class="alert alert-primary" role="alert">
{% translate "No notifications at this time" %} {% translate "No notifications at this time" %}
</div>
{% endfor %}
</ul>
<div class="text-end">
<a href="https://gitlab.com/allianceauth/allianceauth/issues" target="_blank" class="me-1">
<span class="badge" style="background-color: rgb(230 83 40);">
<i class="fab fa-gitlab" aria-hidden="true"></i>
{% translate 'Powered by GitLab' %}
</span>
</a>
<a href="https://discord.com/invite/fjnHAmk" target="_blank">
<span class="badge" style="background-color: rgb(110 133 211);">
<i class="fab fa-discord" aria-hidden="true"></i>
{% translate 'Support Discord' %}
</span>
</a>
</div> </div>
</div> {% endfor %}
</ul>
<div class="text-end pt-3">
<a href="https://gitlab.com/allianceauth/allianceauth/issues" target="_blank" class="me-1 text-decoration-none">
<span class="badge" style="background-color: rgb(230 83 40);">
<i class="fab fa-gitlab" aria-hidden="true"></i>
{% translate 'Powered by GitLab' %}
</span>
</a>
<a href="https://discord.com/invite/fjnHAmk" target="_blank" class="text-decoration-none">
<span class="badge" style="background-color: rgb(110 133 211);">
<i class="fab fa-discord" aria-hidden="true"></i>
{% translate 'Support Discord' %}
</span>
</a>
</div> </div>
</div> </div>
</div> </div>
@@ -51,15 +46,14 @@
</div> </div>
{% endif %} {% endif %}
<div class="col-12 align-self-stretch pb-2"> <div class="col-12 mb-3">
<div class="card"> <div class="card">
<div class="card-body d-flex flex-row flex-wrap"> <div class="card-body row">
<div id="aa-dashboard-panel-software-version" class="col-xl-6 col-lg-12 col-md-12 col-sm-12"> <div id="aa-dashboard-panel-software-version" class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
<h4 class="ms-auto me-auto text-center"> {% translate "Software Version" as widget_title %}
{% translate "Software Version" %} {% include "framework/dashboard/widget-title.html" with title=widget_title %}
</h4>
<div class="card-body"> <div>
<ul class="list-group list-group-horizontal w-100" role="group" aria-label="{% translate 'Software Version' %}"> <ul class="list-group list-group-horizontal w-100" role="group" aria-label="{% translate 'Software Version' %}">
<li class="list-group-item w-100"> <li class="list-group-item w-100">
<div class="btn h-100 w-100 cursor-default"> <div class="btn h-100 w-100 cursor-default">
@@ -98,11 +92,10 @@
</div> </div>
<div id="aa-dashboard-panel-task-queue" class="col-xl-6 col-lg-12 col-md-12 col-sm-12"> <div id="aa-dashboard-panel-task-queue" class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
<h4 class="ms-auto me-auto text-center"> {% translate "Task Queue" as widget_title %}
{% translate "Task Queue" %} {% include "framework/dashboard/widget-title.html" with title=widget_title %}
</h4>
<div class="card-body"> <div>
<p> <p>
{% blocktranslate with total=tasks_total|intcomma latest=earliest_task|timesince|default:"?" %} {% blocktranslate with total=tasks_total|intcomma latest=earliest_task|timesince|default:"?" %}
Status of {{ total }} processed tasks • last {{ latest }} Status of {{ total }} processed tasks • last {{ latest }}

View File

@@ -29,56 +29,53 @@
transform: rotate(180deg); transform: rotate(180deg);
} }
{% if user.is_authenticated %} .nav-padding {
.nav-padding { padding-top: {% header_padding_size %} !important;
padding-top: {% header_padding_size %} !important; }
}
{% endif %}
</style> </style>
{% block extra_css %}{% endblock extra_css %} {% block extra_css %}{% endblock extra_css %}
</head> </head>
<body> <body>
{% if user.is_authenticated %} <!-- Top Menu, Blocks don't work in "include" tagged views -->
<!-- Top Menu, Blocks don't work in "include" tagged views --> <nav class="navbar navbar-expand-lg navbar-dark fixed-top bg-primary">
<nav class="navbar navbar-expand-lg navbar-dark fixed-top bg-primary"> <div class="container-fluid justify-content-start">
<div class="container-fluid justify-content-start"> <a class="navbar-brand" data-bs-toggle="collapse" data-bs-target="#sidebar" role="button">
{% if user.is_authenticated %} <i class="fa-solid fa-bars ms-2 me-2"></i>
<a class="navbar-brand" data-bs-toggle="collapse" data-bs-target="#sidebar" role="button"> </a>
<i class="fa-solid fa-bars ms-2 me-2"></i>
</a>
{% endif %}
<div class="navbar-brand"> <div class="navbar-brand">
{% block header_nav_brand %}{{ SITE_NAME }}{% endblock %} {% block header_nav_brand %}{{ SITE_NAME }}{% endblock %}
</div>
<div class="collapse navbar-collapse ms-2 px-2" id="navbarexpand">
<ul id="nav-left" class="nav navbar-nav me-auto">
{% block header_nav_collapse_left %}
{% endblock %}
</ul>
<ul id="nav-right" class="nav navbar-nav">
{% block header_nav_collapse_right %}
{% endblock %}
</ul>
<ul id="nav-right-character-control" class="nav navbar-nav">
{% block header_nav_user_character_control %} <!-- Default to add char and swap main -->
{% include 'allianceauth/top-menu-rh-default.html' %}
{% endblock %}
{% include 'menu/menu-notification-block.html' %}
</ul>
</div>
<a class="navbar-toggler navbar-brand border-0 collapsed" data-bs-toggle="collapse" data-bs-target="#navbarexpand" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation" style="margin-left: auto;">
<i class="fa-solid fa-chevron-up"></i>
</a>
</div> </div>
</nav>
{% endif %} <div class="collapse navbar-collapse ms-2 px-2" id="navbarexpand">
<ul id="nav-left" class="nav navbar-nav me-auto">
{% block header_nav_collapse_left %}
{% endblock %}
</ul>
<ul id="nav-right" class="nav navbar-nav">
{% block header_nav_collapse_right %}
{% endblock %}
</ul>
<ul id="nav-right-character-control" class="nav navbar-nav">
{% block header_nav_user_character_control %} <!-- Default to add char and swap main -->
{% include 'allianceauth/top-menu-rh-default.html' %}
{% endblock %}
{% if user.is_authenticated %}
{% include 'menu/menu-notification-block.html' %}
{% endif %}
</ul>
</div>
<a class="navbar-toggler navbar-brand border-0 collapsed" data-bs-toggle="collapse" data-bs-target="#navbarexpand" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation" style="margin-left: auto;">
<i class="fa-solid fa-chevron-up"></i>
</a>
</div>
</nav>
<!-- End Top Menu --> <!-- End Top Menu -->
<!-- Body --> <!-- Body -->

View File

@@ -1,31 +1,37 @@
{% extends "allianceauth/base-bs5.html" %} {% extends "allianceauth/base-bs5.html" %}
{% load theme_tags %}
{% block page_title %} {% block page_title %}
{{ error_title }} {{ error_title }}
{% endblock page_title %} {% endblock page_title %}
{% block content %} {% block content %}
<div> <div class="d-flex flex-column" style="height: calc(100vh - {% header_padding_size %}); margin-top: -1rem; margin-bottom: -1rem;">
{% include "framework/header/page-header.html" with title=error_title %} <div class="d-flex flex-grow-1 justify-content-center align-items-center">
<div>
{% include "framework/header/page-header.html" with title=error_title %}
<div class="text-center"> <div class="text-center">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="150" width="150"
height="150" height="150"
fill="currentColor" fill="currentColor"
class="bi bi-exclamation-triangle" class="bi bi-exclamation-triangle"
viewBox="0 0 16 16" viewBox="0 0 16 16"
> >
<path <path
d="M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.146.146 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.163.163 0 0 1-.054.06.116.116 0 0 1-.066.017H1.146a.115.115 0 0 1-.066-.017.163.163 0 0 1-.054-.06.176.176 0 0 1 .002-.183L7.884 2.073a.147.147 0 0 1 .054-.057zm1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566z" d="M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.146.146 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.163.163 0 0 1-.054.06.116.116 0 0 1-.066.017H1.146a.115.115 0 0 1-.066-.017.163.163 0 0 1-.054-.06.176.176 0 0 1 .002-.183L7.884 2.073a.147.147 0 0 1 .054-.057zm1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566z"
/> />
<path <path
d="M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995z" d="M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995z"
/> />
</svg> </svg>
</div>
<p class="text-center">{{ error_message }}</p>
</div>
</div> </div>
<p class="text-center">{{ error_message }}</p>
</div> </div>
{% endblock content %} {% endblock content %}

View File

@@ -1,5 +1,5 @@
{% load i18n %} {% load i18n %}
{% if user.is_authenticated %}
<li class="nav-item"> <li class="nav-item">
<a href="{% url 'authentication:add_character' %}" class="nav-link" title="{% translate 'Add Character' %}"> <a href="{% url 'authentication:add_character' %}" class="nav-link" title="{% translate 'Add Character' %}">
<i class="fa-solid fa-plus"></i> <i class="fa-solid fa-plus"></i>
@@ -12,3 +12,10 @@
<span class="d-lg-none d-md-inline m-2">{% translate "Change Main" %}</span> <span class="d-lg-none d-md-inline m-2">{% translate "Change Main" %}</span>
</a> </a>
</li> </li>
{% else %}
<li class="nav-item">
<a href="{% url 'authentication:login' %}" class="nav-link" title="{% translate 'Sign In' %}">
<i class="fa-solid fa-right-to-bracket fa-fw "></i> {% translate "Sign In" %}
</a>
</li>
{% endif %}

View File

@@ -262,7 +262,7 @@ class AuthUtils:
@classmethod @classmethod
def add_permissions_to_user_by_name( def add_permissions_to_user_by_name(
cls, perms: List[str], user: User, disconnect_signals: bool = True cls, perms: list[str], user: User, disconnect_signals: bool = True
) -> User: ) -> User:
"""Add permissions given by name to a user """Add permissions given by name to a user

View File

@@ -10,12 +10,12 @@ class ThemeHook:
def __init__(self, def __init__(self,
name: str, name: str,
description: str, description: str,
css: List[dict], css: list[dict],
js: List[dict], js: list[dict],
css_template: Optional[str] = None, css_template: str | None = None,
js_template: Optional[str] = None, js_template: str | None = None,
html_tags: Optional[str] = "", html_tags: str | None = "",
header_padding: Optional[str] = "4em"): header_padding: str | None = "4em"):
""" """
:param name: Theme python name :param name: Theme python name
:type name: str :type name: str

View File

@@ -1,59 +1,59 @@
{% load i18n %} {% load i18n %}
{% load evelinks %} {% load evelinks %}
<div class="col-12 align-self-stretch py-2"> <div class="col-12 mb-3">
<div class="card h-100"> <div class="card h-100">
<div class="card-body"> <div class="card-body">
<h4 class="card-title text-center">{% translate "Upcoming Timers" %}</h4> {% translate "Upcoming Timers" as widget_title %}
<div class="card-body"> {% include "framework/dashboard/widget-title.html" with title=widget_title %}
<div>
<table class="table">
<thead>
<tr>
<th class="text-center">{% translate "Details" %}</th>
<th class="text-center">{% translate "Timer" %}</th>
<th class="text-center">{% translate "Type" %}</th>
<th class="text-center">{% translate "System" %}</th>
<th class="text-center">{% translate "EVE Time" %}</th>
</tr>
</thead>
<tbody> <div>
{% for timer in timers %} <table class="table">
<tr> <thead>
<td class="text-center"> <tr>
{{ timer.details }} <th class="text-center">{% translate "Details" %}</th>
</td> <th class="text-center">{% translate "Timer" %}</th>
<td class="text-center"> <th class="text-center">{% translate "Type" %}</th>
{{ timer.get_timer_type_display }} <th class="text-center">{% translate "System" %}</th>
</td> <th class="text-center">{% translate "EVE Time" %}</th>
<td class="text-center" nowrap> </tr>
{% if timer.objective == "Hostile" %} </thead>
<div class="badge bg-danger">
{% translate "Hostile" %} <tbody>
</div> {% for timer in timers %}
{% endif %} <tr>
{% if timer.objective == "Friendly" %} <td class="text-center">
<div class="badge bg-primary"> {{ timer.details }}
{% translate "Friendly" %} </td>
</div> <td class="text-center">
{% endif %} {{ timer.get_timer_type_display }}
{% if timer.objective == "Neutral" %} </td>
<div class="badge bg-default"> <td class="text-center" nowrap>
{% translate "Neutral" %} {% if timer.objective == "Hostile" %}
</div> <div class="badge bg-danger">
{% endif %} {% translate "Hostile" %}
</td> </div>
<td class="text-center"><a href="{{ timer.system|dotlan_solar_system_url }}"> {% endif %}
{{ timer.system }} {{ timer.planet_moon }} {% if timer.objective == "Friendly" %}
</a> <div class="badge bg-primary">
</td> {% translate "Friendly" %}
<td class="text-center" nowrap>{{ timer.eve_time | date:"Y-m-d H:i" }}</td> </div>
</tr> {% endif %}
{% endfor %} {% if timer.objective == "Neutral" %}
</tbody> <div class="badge bg-default">
</table> {% translate "Neutral" %}
</div> </div>
{% endif %}
</td>
<td class="text-center"><a href="{{ timer.system|dotlan_solar_system_url }}">
{{ timer.system }} {{ timer.planet_moon }}
</a>
</td>
<td class="text-center" nowrap>{{ timer.eve_time | date:"Y-m-d H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -19,9 +19,9 @@
{% for timer in timers %} {% for timer in timers %}
{% if timer.important == True %} {% if timer.important == True %}
<tr class="danger"> <tr class="bg-danger bg-opacity-25">
{% else %} {% else %}
<tr class="info"> <tr class="bg-info bg-opacity-25">
{% endif %} {% endif %}
<td style="width: 150px;" class="text-center"> <td style="width: 150px;" class="text-center">

View File

@@ -97,7 +97,7 @@ class EditTimerView(TimerManagementView, AddUpdateMixin, UpdateView):
class RemoveTimerView(TimerManagementView, DeleteView): class RemoveTimerView(TimerManagementView, DeleteView):
form_class = TimerForm pass
def dashboard_timers(request): def dashboard_timers(request):

View File

@@ -1,4 +1,5 @@
from typing import List, Iterable, Callable from typing import List
from collections.abc import Iterable, Callable
from django.urls import include from django.urls import include
import esi.urls import esi.urls
@@ -24,8 +25,8 @@ admin.site.site_header = NAME
def urls_from_apps( def urls_from_apps(
apps_hook_functions: Iterable[Callable], public_views_allowed: List[str] apps_hook_functions: Iterable[Callable], public_views_allowed: list[str]
) -> List[URLPattern]: ) -> list[URLPattern]:
"""Return urls from apps and add default decorators.""" """Return urls from apps and add default decorators."""
url_patterns = [] url_patterns = []

View File

@@ -22,7 +22,7 @@ class ItemCounter:
DEFAULT_CACHE_TIMEOUT = 24 * 3600 DEFAULT_CACHE_TIMEOUT = 24 * 3600
def __init__( def __init__(
self, name: str, minimum: Optional[int] = None, redis: Optional[Redis] = None self, name: str, minimum: int | None = None, redis: Redis | None = None
) -> None: ) -> None:
if not name: if not name:
raise ValueError("Must define a name") raise ValueError("Must define a name")
@@ -60,6 +60,6 @@ class ItemCounter:
except ValueError: except ValueError:
pass pass
def value(self) -> Optional[int]: def value(self) -> int | None:
"""Return current value or None if not yet initialized.""" """Return current value or None if not yet initialized."""
return cache.get(self._cache_key) return cache.get(self._cache_key)

View File

@@ -1,7 +1,7 @@
PROTOCOL=https:// PROTOCOL=https://
AUTH_SUBDOMAIN=%AUTH_SUBDOMAIN% AUTH_SUBDOMAIN=%AUTH_SUBDOMAIN%
DOMAIN=%DOMAIN% DOMAIN=%DOMAIN%
AA_DOCKER_TAG=registry.gitlab.com/allianceauth/allianceauth/auth:v4.0.0 AA_DOCKER_TAG=registry.gitlab.com/allianceauth/allianceauth/auth:v4.2.2
# Nginx Proxy Manager # Nginx Proxy Manager
PROXY_HTTP_PORT=80 PROXY_HTTP_PORT=80

View File

@@ -1,5 +1,5 @@
FROM python:3.11-slim FROM python:3.11-slim
ARG AUTH_VERSION=v4.0.0 ARG AUTH_VERSION=v4.2.2
ARG AUTH_PACKAGE=allianceauth==${AUTH_VERSION} ARG AUTH_PACKAGE=allianceauth==${AUTH_VERSION}
ENV AUTH_USER=allianceauth ENV AUTH_USER=allianceauth
ENV AUTH_GROUP=allianceauth ENV AUTH_GROUP=allianceauth
@@ -9,21 +9,21 @@ ENV AUTH_HOME=/home/allianceauth
# Setup user and directory permissions # Setup user and directory permissions
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
RUN groupadd -g 61000 ${AUTH_GROUP} RUN groupadd -g 61000 ${AUTH_GROUP} && \
RUN useradd -g 61000 -l -M -s /bin/false -u 61000 ${AUTH_USER} useradd -g 61000 -l -m -s /bin/false -u 61000 ${AUTH_USER}
RUN mkdir -p ${STATIC_BASE} \
&& chown ${AUTH_USERGROUP} ${STATIC_BASE} \
&& mkdir -p ${AUTH_HOME} \
&& chown ${AUTH_USERGROUP} ${AUTH_HOME}
# Install build dependencies RUN mkdir -p ${STATIC_BASE}/myauth/static \
RUN apt-get update && apt-get upgrade -y && apt-get install -y \ && chown ${AUTH_USERGROUP} ${STATIC_BASE}/myauth/static
libmariadb-dev gcc git pkg-config
# Install python dependencies # Install Build Dependencies
RUN pip install --upgrade pip RUN apt-get update \
RUN pip install wheel gunicorn && apt-get upgrade -y \
RUN pip install ${AUTH_PACKAGE} && apt-get install -y --no-install-recommends libmariadb-dev gcc git pkg-config \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install AA and Dependencies
RUN pip install --no-cache-dir ${AUTH_PACKAGE} gunicorn
# Switch to non-root user # Switch to non-root user
USER ${AUTH_USER} USER ${AUTH_USER}
@@ -33,7 +33,6 @@ WORKDIR ${AUTH_HOME}
RUN allianceauth start myauth RUN allianceauth start myauth
COPY /allianceauth/project_template/project_name/settings/local.py ${AUTH_HOME}/myauth/myauth/settings/local.py COPY /allianceauth/project_template/project_name/settings/local.py ${AUTH_HOME}/myauth/myauth/settings/local.py
RUN allianceauth update myauth RUN allianceauth update myauth
RUN mkdir -p ${STATIC_BASE}/myauth/static
RUN echo 'alias auth="python $AUTH_HOME/myauth/manage.py"' >> ~/.bashrc && \ RUN echo 'alias auth="python $AUTH_HOME/myauth/manage.py"' >> ~/.bashrc && \
source ~/.bashrc source ~/.bashrc

View File

@@ -9,6 +9,10 @@ from django.conf import settings # noqa
app = Celery('myauth') app = Celery('myauth')
# Automatically try to establish the connection to the AMQP broker on
# Celery startup if it is unavailable.
app.conf.broker_connection_retry_on_startup = True
# Using a string here means the worker don't have to serialize # Using a string here means the worker don't have to serialize
# the configuration object to child processes. # the configuration object to child processes.
app.config_from_object('django.conf:settings') app.config_from_object('django.conf:settings')

View File

@@ -1,20 +1,28 @@
server { events {}
listen 80; http {
location = /favicon.ico { access_log off; log_not_found off; } include mime.types;
location /static { default_type application/octet-stream;
alias /var/www/myauth/static;
autoindex off;
}
location /robots.txt { sendfile on;
alias /var/www/myauth/static/robots.txt;
}
location / { server {
proxy_pass http://allianceauth_gunicorn:8000; listen 80;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location = /favicon.ico { access_log off; log_not_found off; }
proxy_set_header Host $host; location /static {
proxy_set_header X-Real-IP $remote_addr; alias /var/www/myauth/static;
proxy_redirect off; autoindex off;
}
location /robots.txt {
alias /var/www/myauth/static/robots.txt;
}
location / {
proxy_pass http://allianceauth_gunicorn:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
}
} }
} }

View File

@@ -1,6 +1,8 @@
ARG AA_DOCKER_TAG ARG AA_DOCKER_TAG
FROM $AA_DOCKER_TAG FROM $AA_DOCKER_TAG
RUN cd /home/allianceauth WORKDIR ${AUTH_HOME}
COPY /conf/requirements.txt requirements.txt COPY /conf/requirements.txt requirements.txt
RUN pip install -r requirements.txt RUN --mount=type=cache,target=~/.cache \
pip install -r requirements.txt

View File

@@ -62,10 +62,10 @@ services:
max-file: "5" max-file: "5"
nginx: nginx:
image: nginx:1.25 image: nginx:stable
restart: always restart: always
volumes: volumes:
- ./conf/nginx.conf:/etc/nginx/conf.d/default.conf - ./conf/nginx.conf:/etc/nginx/nginx.conf
- static-volume:/var/www/myauth/static - static-volume:/var/www/myauth/static
depends_on: depends_on:
- allianceauth_gunicorn - allianceauth_gunicorn

View File

@@ -36,7 +36,7 @@ from allianceauth.framework.api.evecharacter import get_user_from_evecharacter
user = get_user_from_evecharacter(character=my_evecharacter) user = get_user_from_evecharacter(character=my_evecharacter)
``` ```
Now, `user` is a `User` object, or the sentinel username (see [get_sentinel_user](#get-sentinel-user)) Now, `user` is a `User` object, or the sentinel username (see [get_sentinel_user](#get_sentinel_user))
if the `EveCharacter` has no user. if the `EveCharacter` has no user.
## User API ## User API
@@ -88,7 +88,7 @@ main_character = get_main_character_name_from_user(user=my_user)
Now, `main_character` is a `string` containing the user's main character name. Now, `main_character` is a `string` containing the user's main character name.
If the user has no main character, the username will be returned. If the user is `None`, If the user has no main character, the username will be returned. If the user is `None`,
the sentinel username (see [get_sentinel_user](#get-sentinel-user)) will be returned. the sentinel username (see [get_sentinel_user](#get_sentinel_user)) will be returned.
### get_sentinel_user ### get_sentinel_user

View File

@@ -8,7 +8,7 @@ have to load specific CSS or JavaScript yourself.
These bundles include DataTables CSS and JS, jQuery Datepicker CSS and JS, jQueryUI CSS and JS, and more. These bundles include DataTables CSS and JS, jQuery Datepicker CSS and JS, jQueryUI CSS and JS, and more.
A full list of bundles we provide can be found here: https://gitlab.com/allianceauth/allianceauth/-/tree/master/allianceauth/templates/bundles A full list of bundles we provide can be found here: <https://gitlab.com/allianceauth/allianceauth/-/tree/master/allianceauth/templates/bundles>
To use a bundle, you can use the following code in your template (Example for jQueryUI): To use a bundle, you can use the following code in your template (Example for jQueryUI):
@@ -28,6 +28,27 @@ To ensure a unified style language throughout Alliance Auth and Community Apps,
we also provide a couple of template partials. This collection is bound to grow over we also provide a couple of template partials. This collection is bound to grow over
time, so best have an eye on this page. time, so best have an eye on this page.
### Dashboard Widget Title
To ensure the dashboard widgets have a unified style, we provide a template partial for the widget title.
To use it, you can use the following code in your dashboard widget template:
```django
<div id="my-app-dashboard-widget" class="col-12 mb-3">
<div class="card">
<div class="card-body">
{% translate "My Widget Title" as widget_title %}
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
<div>
<p>My widget content</p>
</div>
</div>
</div>
</div>
```
### Page Header ### Page Header
On some pages you want to have a page header. To make this easier, we provide a template partial for this. On some pages you want to have a page header. To make this easier, we provide a template partial for this.
@@ -38,7 +59,8 @@ To use it, you can use the following code in your template:
{% block content %} {% block content %}
<div> <div>
{% translate "My Page Header" as page_header %} {% translate "My Page Header" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %} {% translate "My Page Header Subtitle" as optional_subtitle %}
{% include "framework/header/page-header.html" with title=page_header subtitle=optional_subtitle %}
<p>My page content</p> <p>My page content</p>
</div> </div>

View File

@@ -122,10 +122,6 @@ class MyService(ServiceHook):
All of your apps defined urlpatterns will then be included in the `URLconf` when the core application starts. All of your apps defined urlpatterns will then be included in the `URLconf` when the core application starts.
#### self.service_ctrl_template
This is provided as a courtesy and defines the default template to be used with [render_service_ctrl](#render_service_ctrl). You are free to redefine or not use this variable at all.
#### title #### title
This is a property which provides a user-friendly display of your service's name. It will usually do a reasonably good job unless your service name has punctuation or odd capitalization. If this is the case, you should override this method and return a string. This is a property which provides a user-friendly display of your service's name. It will usually do a reasonably good job unless your service name has punctuation or odd capitalization. If this is the case, you should override this method and return a string.
@@ -134,7 +130,7 @@ This is a property which provides a user-friendly display of your service's name
#### self.service_ctrl_template #### self.service_ctrl_template
This is provided as a courtesy and defines the default template to be used with [render_service_ctrl](#render-service-ctrl). You are free to redefine or not use this variable at all. This is provided as a courtesy and defines the default template to be used with [render_service_ctrl](#render_service_ctrl). You are free to redefine or not use this variable at all.
#### delete_user #### delete_user

View File

@@ -18,6 +18,8 @@ The `MenuItemHook` class specifies some parameters/instance variables required f
:undoc-members: :undoc-members:
``` ```
## Parameters
### text ### text
The text shown as menu item, e.g., usually the name of the app. The text shown as menu item, e.g., usually the name of the app.

View File

@@ -12,6 +12,8 @@ def register_urls():
return UrlHook(app_name.urls, 'app_name', r^'app_name/') return UrlHook(app_name.urls, 'app_name', r^'app_name/')
``` ```
### Parameters
#### urls #### urls
The urls module to include. See [the Django docs](https://docs.djangoproject.com/en/dev/topics/http/urls/#example) for designing urlpatterns. The urls module to include. See [the Django docs](https://docs.djangoproject.com/en/dev/topics/http/urls/#example) for designing urlpatterns.

View File

@@ -50,6 +50,7 @@ Update your auth project's settings file, inputting the server ID as `DISCORD_GU
:::{note} :::{note}
If you already have a Discord server, skip the creation step, but be sure to retrieve the server ID If you already have a Discord server, skip the creation step, but be sure to retrieve the server ID
::: :::
### Registering an Application ### Registering an Application
Navigate to the [Discord Developers site.](https://discord.com/developers/applications/me) Press the plus sign to create a new application. Navigate to the [Discord Developers site.](https://discord.com/developers/applications/me) Press the plus sign to create a new application.
@@ -112,6 +113,7 @@ Role names on Discord are case-sensitive, while reserved group names on Auth are
.. seealso:: .. seealso::
For more information see :ref:`ref-reserved-group-names`. For more information see :ref:`ref-reserved-group-names`.
``` ```
## Tasks ## Tasks
The Discord service contains a number of tasks that can be run to manually perform updates to all users. The Discord service contains a number of tasks that can be run to manually perform updates to all users.
@@ -132,6 +134,7 @@ Name Description
`update_all` Update groups, nicknames, usernames of all users `update_all` Update groups, nicknames, usernames of all users
======================== ==================================================== ======================== ====================================================
``` ```
:::{note} :::{note}
Depending on how many users you have, running these tasks can take considerable time to finish. You can calculate roughly 1 sec per user for all tasks, except update_all, which needs roughly 3 secs per user. Depending on how many users you have, running these tasks can take considerable time to finish. You can calculate roughly 1 sec per user for all tasks, except update_all, which needs roughly 3 secs per user.
::: :::
@@ -156,6 +159,7 @@ Name Description
`DISCORD_TASKS_MAX_RETRIES` max retries of tasks after an error occurred `3` `DISCORD_TASKS_MAX_RETRIES` max retries of tasks after an error occurred `3`
=================================== ============================================================================================= ======= =================================== ============================================================================================= =======
``` ```
## Permissions ## Permissions
To use this service, users will require some of the following. To use this service, users will require some of the following.
@@ -167,6 +171,7 @@ To use this service, users will require some of the following.
| discord.access_discord | None | Can Access the Discord Service | | discord.access_discord | None | Can Access the Discord Service |
+---------------------------------------+------------------+--------------------------------------------------------------------------+ +---------------------------------------+------------------+--------------------------------------------------------------------------+
``` ```
## Troubleshooting ## Troubleshooting
### "Unknown Error" on Discord site when activating service ### "Unknown Error" on Discord site when activating service

View File

@@ -1,4 +1,4 @@
# Mumble # Mumble (Docker)
An alternate install guide for Mumble using Docker, better suited to an Alliance Auth Docker install An alternate install guide for Mumble using Docker, better suited to an Alliance Auth Docker install
@@ -18,7 +18,7 @@ MUMBLE_URL = "mumble.example.com"
Add the following lines to your `.env` file Add the following lines to your `.env` file
```env ```bash
# Mumble # Mumble
MUMBLE_SUPERUSER_PASSWORD = superuser_password MUMBLE_SUPERUSER_PASSWORD = superuser_password
MUMBLE_ICESECRETWRITE = icesecretwrite MUMBLE_ICESECRETWRITE = icesecretwrite
@@ -67,7 +67,7 @@ Add the following to your `docker-compose.yml` under the `services:` section
max-file: "5" max-file: "5"
mumble-authenticator: mumble-authenticator:
build build:
context: . context: .
dockerfile: ./mumble-authenticator/Dockerfile dockerfile: ./mumble-authenticator/Dockerfile
restart: always restart: always

View File

@@ -29,6 +29,7 @@ Currently, the following services support custom name formats:
| Xenforo | Username | ``{character_name}`` | | Xenforo | Username | ``{character_name}`` |
+-------------+-----------+-------------------------------------+ +-------------+-----------+-------------------------------------+
``` ```
:::{note} :::{note}
It's important to note here, before we get into what you can do with a name formatter, that before the generated name is passed off to the service to create an account it will be sanitized to remove characters (the letters and numbers etc.) that the service cannot support. This means that, despite what you configured, the service may display something different. It is up to you to test your formatter and understand how your format may be disrupted by a certain services sanitization function. It's important to note here, before we get into what you can do with a name formatter, that before the generated name is passed off to the service to create an account it will be sanitized to remove characters (the letters and numbers etc.) that the service cannot support. This means that, despite what you configured, the service may display something different. It is up to you to test your formatter and understand how your format may be disrupted by a certain services sanitization function.
::: :::

View File

@@ -1,4 +1,4 @@
# Openfire # Openfire (Docker)
An alternate install guide for Openfire using Docker, better suited to an Alliance Auth Docker install An alternate install guide for Openfire using Docker, better suited to an Alliance Auth Docker install
@@ -25,7 +25,7 @@ BROADCAST_SERVICE_NAME = "broadcast"
Add the following lines to your `.env` file Add the following lines to your `.env` file
```env ```bash
# Openfire # Openfire
OPENFIRE_SECRET_KEY = superuser_password OPENFIRE_SECRET_KEY = superuser_password
BROADCAST_USER_PASSWORD = icesecretwrite BROADCAST_USER_PASSWORD = icesecretwrite

View File

@@ -21,7 +21,7 @@ PHPBB3_URL = ''
DATABASES['phpbb3'] = { DATABASES['phpbb3'] = {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': 'alliance_forum', 'NAME': 'alliance_forum',
'USER': 'allianceserver-phpbb3', 'USER': 'allianceserver',
'PASSWORD': 'password', 'PASSWORD': 'password',
'HOST': '127.0.0.1', 'HOST': '127.0.0.1',
'PORT': '3306', 'PORT': '3306',

View File

@@ -1,4 +1,4 @@
# TeamSpeak 3 # TeamSpeak 3 (Docker)
## Overview ## Overview
@@ -34,7 +34,7 @@ CELERYBEAT_SCHEDULE['run_ts3_group_update'] = {
Add the following lines to your `.env` file Add the following lines to your `.env` file
```env ```bash
# Temspeak # Temspeak
TEAMSPEAK3_SERVERQUERY_USER = "serverquery" TEAMSPEAK3_SERVERQUERY_USER = "serverquery"
TEAMSPEAK3_SERVERQUERY_PASSWORD = "" TEAMSPEAK3_SERVERQUERY_PASSWORD = ""

View File

@@ -58,7 +58,7 @@ There are a handful of ways to add packages:
Using a custom docker image is the preferred approach, as it gives you the stability of packages only changing when you tell them to, along with packages not having to be downloaded every time your container restarts Using a custom docker image is the preferred approach, as it gives you the stability of packages only changing when you tell them to, along with packages not having to be downloaded every time your container restarts
1. Add each additional package that you want to install to a single line in `conf/requirements.txt`. It is recommended, but not required, that you include a version number as well. This will keep your packages from magically updating. You can lookup packages on <https://package.wiki>, and copy everything after `pip install` from the top of the page to use the most recent version. It should look something like `allianceauth-signal-pings==0.0.7`. Every entry in this file should be on a separate line 1. Add each additional package that you want to install to a single line in `conf/requirements.txt`. It is recommended, but not required, that you include a version number as well. This will keep your packages from magically updating. You can lookup packages on <https://pypi.org>, and copy from the title at the top of the page to use the most recent version. It should look something like `allianceauth-signal-pings==0.0.7`. Every entry in this file should be on a separate line
1. Modify `docker-compose.yml`, as follows. 1. Modify `docker-compose.yml`, as follows.
* Comment out the `image` line under `allianceauth` * Comment out the `image` line under `allianceauth`
* Uncomment the `build` section * Uncomment the `build` section

View File

@@ -23,13 +23,13 @@ Take a complete backup of your local.py, docker-compose and SQL database.
`docker compose down` `docker compose down`
Replace your conf/nginx.conf with the contents of <https://gitlab.com/allianceauth/allianceauth/-/blob/v4.x/docker/conf/nginx.conf> Replace your conf/nginx.conf with the contents of <https://gitlab.com/allianceauth/allianceauth/-/raw/v4.x/docker/conf/nginx.conf>
Replace your docker-compose.yml with the contents of <https://gitlab.com/allianceauth/allianceauth/-/raw/v4.x/docker/docker-compose.yml> Replace your docker-compose.yml with the contents of <https://gitlab.com/allianceauth/allianceauth/-/raw/v4.x/docker/docker-compose.yml>
V3.x installs likely used a dedicated database for Nginx Proxy Manager, you can either setup NPM again without a database, or uncomment the sections noted to maintain this configuration V3.x installs likely used a dedicated database for Nginx Proxy Manager, you can either setup NPM again without a database, or uncomment the sections noted to maintain this configuration
```docker-compose ```docker
proxy: proxy:
... ...
# Uncomment this section to use a dedicated database for Nginx Proxy Manager # Uncomment this section to use a dedicated database for Nginx Proxy Manager
@@ -64,7 +64,7 @@ V3.x installs likely used a dedicated database for Nginx Proxy Manager, you can
You will need to add some entries to your .env file You will need to add some entries to your .env file
```env ```bash
AA_DB_CHARSET=utf8mb4 AA_DB_CHARSET=utf8mb4
GF_SECURITY_ADMIN_USERNAME=admin GF_SECURITY_ADMIN_USERNAME=admin
``` ```

View File

@@ -181,13 +181,17 @@ If you don't plan on running the database on the same server as auth you still n
### Redis and Other Tools ### Redis and Other Tools
A few extra utilities are also required for installation of packages. A few extra utilities are also required for the installation of packages.
::::{tabs} ::::{tabs}
:::{group-tab} Ubuntu 2004, 2204 :::{group-tab} Ubuntu 2004, 2204
```shell ```shell
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install unzip git redis-server curl libssl-dev libbz2-dev libffi-dev build-essential pkg-config sudo apt-get install unzip git redis-server curl libssl-dev libbz2-dev libffi-dev build-essential pkg-config
``` ```

View File

@@ -11,21 +11,28 @@ If you're using a small VPS to host services with very limited memory, consider
::::{tabs} ::::{tabs}
:::{group-tab} Ubuntu 2004, 2204 :::{group-tab} Ubuntu 2004, 2204
```shell ```shell
apt-get install apache2 apt-get install apache2
``` ```
::: :::
:::{group-tab} CentOS 7 :::{group-tab} CentOS 7
```shell ```shell
yum install httpd yum install httpd
``` ```
::: :::
:::{group-tab} CentOS Stream 8 :::{group-tab} CentOS Stream 8
```shell ```shell
dnf install httpd dnf install httpd
``` ```
::: :::
:::{group-tab} CentOS Stream 9 :::{group-tab} CentOS Stream 9
```shell ```shell
systemctl enable httpd systemctl enable httpd
systemctl start httpd systemctl start httpd
@@ -36,7 +43,6 @@ systemctl start httpd
CentOS 7, Stream 8, Stream 9 CentOS 7, Stream 8, Stream 9
## Configuration ## Configuration
### Permissions ### Permissions
@@ -116,7 +122,7 @@ a2dissite 000-default.conf
## Sample Config File ## Sample Config File
```ini ```apacheconf
<VirtualHost *:80> <VirtualHost *:80>
ServerName auth.example.com ServerName auth.example.com

View File

@@ -6,11 +6,47 @@ In its default configuration, your auth project logs INFO and higher messages to
To record DEBUG messages in the log file, alter a setting in your auth project's settings file: `LOGGING['handlers']['log_file']['level'] = 'DEBUG'`. After restarting gunicorn and celery, your log file will record all logging messages. To record DEBUG messages in the log file, alter a setting in your auth project's settings file: `LOGGING['handlers']['log_file']['level'] = 'DEBUG'`. After restarting gunicorn and celery, your log file will record all logging messages.
## Steps to Check Logs for Errors
### Locate the Logs
The logs are located within the `myauth/log/` directory of your Alliance Auth project.
### Access the Logs
Use a text editor or terminal commands (like `tail -f <filename>`) to open the relevant log files.
_(The `tail -f` command displays the last few lines of a file and then continues to monitor the file, printing any new lines that are added in real-time. Useful to watch while actively testing a troublesome feature while troubleshooting.)_
Consider the following:
`allianceauth.log` is the primary log for general troubleshooting. Tracks user actions, changes made, and potential errors.
`worker.log` is important for issues related to background tasks.(Such as services(Discord)).
`beat.log` if you suspect scheduler problems.
`gunicorn.log` for web-specific or Gunicorn worker errors.
### Search for Errors
Look for keywords like `ERROR`, `WARNING`, `EXCEPTION`, or `CRITICAL`. Examine timestamps to correlate errors with user actions or events. Read the error messages carefully for clues about the problem's nature.
Troubleshooting Tips:
**Filter Logs:** Use tools like `grep` to filter logs based on keywords or timeframes, making it easier to focus on relevant information.
**Example**: `tail -f worker.log | grep -i 'discord'`. This will isolate lines containing discord. Making it easier to see among the other logs.
**Debug Mode:** For in-depth troubleshooting, temporarily enable DEBUG logging in your `local.py` to get more detailed messages. Remember to set it back to `False` after debugging.
**Important Note: Before sharing logs publicly, sanitize any sensitive information such as usernames, passwords, or API keys.**
## Common Problems ## Common Problems
### I'm getting error 500 when trying to connect to the website on a new installation ### I'm getting error 500 when trying to connect to the website on a new installation
*Great.* Error 500 is the generic message given by your web server when *anything* breaks. The actual error message is hidden in one of your auth project's log files. Read them to identify it. _Great._ Error 500 is the generic message given by your web server when _anything_ breaks. The actual error message is hidden in one of your auth project's log files. Read them to identify it.
### Failed to configure log handler ### Failed to configure log handler

View File

@@ -1,13 +0,0 @@
# Gunicorn
## Number of workers
The default installation will have 3 workers configured for Gunicorn. This will be fine on most systems, but if your system as more than one core than you might want to increase the number of workers to get better response times. Note that more workers will also need more RAM though.
The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests `(2 x $num_cores) + 1` for the number of workers. So for example, if you have 2 cores, you want 2 x 2 + 1 = 5 workers. See [here](https://docs.gunicorn.org/en/stable/design.html#how-many-workers) for the official discussion on this topic.
For example, to get 5 workers change the setting `--workers=5` in your `supervisor.conf` file and then reload the supervisor with the following command to activate the change (Ubuntu):
```shell
systemctl restart supervisor
```

View File

@@ -9,7 +9,7 @@ Tuning usually has benefits and costs and should only be performed by experience
:::{toctree} :::{toctree}
:maxdepth: 1 :maxdepth: 1
gunicorn web
celery celery
redis redis
python python

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,156 @@
# Web Tuning
## Gunicorn
### Number of workers
The default installation will have 3 workers configured for Gunicorn. This will be fine on most systems, but if your system as more than one core than you might want to increase the number of workers to get better response times. Note that more workers will also need more RAM though.
The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests `(2 x $num_cores) + 1` for the number of workers. So for example, if you have 2 cores, you want 2 x 2 + 1 = 5 workers. See [here](https://docs.gunicorn.org/en/stable/design.html#how-many-workers) for the official discussion on this topic.
::::{tabs}
:::{group-tab} Ubuntu 2204, 2404
To have 5 workers change the setting `--workers=x` to `--workers=5` in your `supervisor.conf` file and then reload the supervisor with the following command to activate the change
```shell
systemctl restart supervisor
```
:::
:::{group-tab} CentOS / RHEL
To have 5 workers change the setting `--workers=x` to `--workers=5` in your `supervisor.conf` file and then reload the supervisor with the following command to activate the change
```shell
systemctl restart supervisor
```
:::
:::{group-tab} Docker Compose
To have 5 workers change the setting `--workers=3` to `--workers=5` in your `docker-compose.yml` file and then restart the container as follows
```shell
docker compose up -d
```
:::
::::
## nginx
### nginx repo
We can use the Nginx repositories for a slightly more cutting edge version of Nginx, with more features. Install it to enable the below features
::::{tabs}
:::{group-tab} Ubuntu 2204, 2404
<https://nginx.org/en/linux_packages.html#Ubuntu>
:::
:::{group-tab} CentOS / RHEL
<https://nginx.org/en/linux_packages.html#RHEL>
:::
:::{group-tab} Docker
No package necessary, simply increase `docker compose pull` and `docker compose up -d` to update.
:::
::::
### Brotli Compression
Brotli is a modern compression algorithm designed for the web. Use this with Pre-Compression and E2E Cloudflare compression for best results.
::::{tabs}
:::{group-tab} Ubuntu 2204, 2404
sudo apt update
sudo apt install libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static
:::
:::{group-tab} CentOS
WIP
:::
:::{group-tab} Docker
Pull a custom dockerfile
```bash
mkdir nginx
curl -o my-nginx/Dockerfile https://raw.githubusercontent.com/nginxinc/docker-nginx/master/modules/Dockerfile
```
Replace the nginx service in your docker-compose as follows
```dockerfile
nginx:
build:
context: ./nginx/
args:
ENABLED_MODULES: brotli
restart: always
volumes:
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- static-volume:/var/www/myauth/static
depends_on:
- allianceauth_gunicorn
logging:
driver: "json-file"
options:
max-size: "10Mb"
max-file: "5"
```
:::
::::
Modify your nginx.conf as follows
```nginx
load_module modules/ngx_http_brotli_static_module.so;
load_module modules/ngx_http_brotli_filter_module.so;
...
http {
...
server {
...
location /static {
...
brotli_static on;
brotli_types application/javascript text/css font/woff2 image/png image/svg+xml font/woff image/gif;
brotli_comp_level 11;
}
...
location / {
...
brotli on;
brotli_comp_level 4;
}
}
}
```
### Staticfile Pre-Compression
We can use a small library to pre-compress staticfiles for Nginx to deliver.
```shell
pip install django-static-compress
```
Add the following lines to local.py
```python
# Tuning / Compression
STORAGES = {
"staticfiles": {
"BACKEND": "static_compress.CompressedStaticFilesStorage",
},
}
STATIC_COMPRESS_FILE_EXTS = ['js', 'css', 'woff2', 'png', 'svg', 'woff', 'gif']
STATIC_COMPRESS_METHODS = ['gz', 'br']
STATIC_COMPRESS_KEEP_ORIGINAL = True
STATIC_COMPRESS_MIN_SIZE_KB = 1
```
## Cloudflare
### Brotli E2E Compression
Soon to be turned on by default. Refer to <https://developers.cloudflare.com/speed/optimization/content/brotli/enable/>
In order for cloudflare to seamlessly pass on your brotli compressed pages (End to End), ensure minification, rocket loader and other features are _off_ <https://developers.cloudflare.com/speed/optimization/content/brotli/enable/#notes-about-end-to-end-compression>. Else cloudflare will need to uncompress, modify, then recompress your pages.

View File

@@ -1,86 +1,85 @@
[build-system] [build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi" build-backend = "flit_core.buildapi"
requires = [
"flit-core<4,>=3.2",
]
[project] [project]
name = "allianceauth" name = "allianceauth"
dynamic = ["version", "description"]
readme = "README.md" readme = "README.md"
license = {file = "LICENSE"}
requires-python = ">=3.8"
authors = [
{ name = "Alliance Auth", email = "adarnof@gmail.com" },
]
keywords = [ keywords = [
"allianceauth", "allianceauth",
"eveonline", "eveonline",
] ]
license = { file = "LICENSE" }
authors = [
{ name = "Alliance Auth", email = "adarnof@gmail.com" },
]
requires-python = ">=3.10"
classifiers = [ classifiers = [
"Environment :: Web Environment", "Environment :: Web Environment",
"Framework :: Celery",
"Framework :: Django", "Framework :: Django",
"Framework :: Django :: 4.2", "Framework :: Django :: 4.2",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
"Operating System :: POSIX :: Linux", "Operating System :: POSIX :: Linux",
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
] ]
dynamic = [
"description",
"version",
]
dependencies = [ dependencies = [
"bcrypt", "bcrypt",
"beautifulsoup4", "beautifulsoup4",
"celery<6,>=5.2",
"celery-once>=3.0.1", "celery-once>=3.0.1",
"celery>=5.2.0,<6", "django<5.2,>=5.1",
'django-bootstrap5>=23.3',
"django-bootstrap-form", "django-bootstrap-form",
"django-celery-beat>=2.3.0", "django-bootstrap5>=23.3",
"django-esi>=5.0.0", "django-celery-beat>=2.7",
"django-redis>=5.2.0", "django-esi>=5",
"django-registration>=3.3,<3.4", "django-redis>=5.2",
"django-registration<3.4,>=3.3",
"django-sortedm2m", "django-sortedm2m",
"django>=4.2,<5",
"dnspython", "dnspython",
"mysqlclient>=2.1.0", "mysqlclient>=2.1",
"openfire-restapi", "openfire-restapi",
"packaging>=21.0", "packaging>=21",
"passlib", "passlib",
"pydiscourse", "pydiscourse",
"python-slugify>=1.2", "python-slugify>=1.2",
"redis>=4.0.0", "redis>=4",
"requests-oauthlib",
"requests>=2.9.1", "requests>=2.9.1",
"requests-oauthlib",
"semantic-version", "semantic-version",
"slixmpp", "slixmpp",
] ]
optional-dependencies.docs = [
[project.optional-dependencies] "myst-parser",
test = [ "sphinx",
"sphinx-copybutton",
"sphinx-rtd-theme<3,>=2",
"sphinx-tabs",
"sphinxcontrib-django",
]
optional-dependencies.test = [
"coverage>=4.3.1", "coverage>=4.3.1",
"django-webtest", "django-webtest",
"requests-mock>=1.2.0" "requests-mock>=1.2",
] ]
docs = [ urls.Documentation = "https://allianceauth.readthedocs.io/"
"sphinx", urls.Homepage = "https://gitlab.com/allianceauth/allianceauth"
"sphinx_rtd_theme>=2.0.0,<3.0.0", urls.Source = "https://gitlab.com/allianceauth/allianceauth"
"myst-parser", urls.Tracker = "https://gitlab.com/allianceauth/allianceauth/-/issues"
"sphinxcontrib-django", scripts.allianceauth = "allianceauth.bin.allianceauth:main"
"sphinx-copybutton",
"sphinx-tabs",
]
[project.scripts]
allianceauth = "allianceauth.bin.allianceauth:main"
[project.urls]
Homepage = "https://gitlab.com/allianceauth/allianceauth"
Documentation = "https://allianceauth.readthedocs.io/"
Source = "https://gitlab.com/allianceauth/allianceauth"
Tracker = "https://gitlab.com/allianceauth/allianceauth/-/issues"
[tool.flit.module] [tool.flit.module]
name = "allianceauth" name = "allianceauth"
@@ -94,8 +93,12 @@ sections = [
"DJANGO", "DJANGO",
"ESI", "ESI",
"FIRSTPARTY", "FIRSTPARTY",
"LOCALFOLDER" "LOCALFOLDER",
]
known_esi = [
"esi",
]
known_django = [
"django",
] ]
known_esi = ["esi"]
known_django = ["django"]
skip_gitignore = true skip_gitignore = true

View File

@@ -1,19 +1,18 @@
[tox] [tox]
isolated_build = True isolated_build = true
skipsdist = true skipsdist = true
usedevelop = true usedevelop = true
envlist = py{38,39,310,311,312}-{all,core}, docs envlist = py{310,311,312,313}-{all,core}, docs
[testenv] [testenv]
setenv = setenv =
all: DJANGO_SETTINGS_MODULE = tests.settings_all all: DJANGO_SETTINGS_MODULE = tests.settings_all
core: DJANGO_SETTINGS_MODULE = tests.settings_core core: DJANGO_SETTINGS_MODULE = tests.settings_core
basepython = basepython =
py38: python3.8
py39: python3.9
py310: python3.10 py310: python3.10
py311: python3.11 py311: python3.11
py312: python3.12 py312: python3.12
py313: python3.13
deps= deps=
coverage coverage
install_command = pip install -e ".[test]" -U {opts} {packages} install_command = pip install -e ".[test]" -U {opts} {packages}
@@ -25,7 +24,7 @@ commands =
[testenv:docs] [testenv:docs]
description = invoke sphinx-build to build the HTML docs description = invoke sphinx-build to build the HTML docs
basepython = python3.11 basepython = python3.12
install_command = pip install -e ".[docs]" -U {opts} {packages} install_command = pip install -e ".[docs]" -U {opts} {packages}
commands = commands =
sphinx-build -T -E -b html -d "{toxworkdir}/docs_doctree" -D language=en docs "{toxworkdir}/docs_out" {posargs} sphinx-build -T -E -b html -d "{toxworkdir}/docs_doctree" -D language=en docs "{toxworkdir}/docs_out" {posargs}