mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-13 18:46:25 +01:00
Compare commits
160 Commits
v4.0.0b2
...
63fb449060
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63fb449060 | ||
|
|
235675fa9b | ||
|
|
a065f043eb | ||
|
|
0839920032 | ||
|
|
29ad4acff7 | ||
|
|
3a5b84d1f9 | ||
|
|
bbcb94021e | ||
|
|
d50f13528b | ||
|
|
c88521af88 | ||
|
|
2bd5ff8723 | ||
|
|
84484cebcb | ||
|
|
5ee34fcb2d | ||
|
|
046473def1 | ||
|
|
6aaba2bf3d | ||
|
|
acff3695bc | ||
|
|
43ec8514aa | ||
|
|
4c629b193f | ||
|
|
c651da4011 | ||
|
|
da382cffd1 | ||
|
|
4ecfc3afd8 | ||
|
|
4eb7dbbe62 | ||
|
|
c96ba65296 | ||
|
|
ff2f60f7f3 | ||
|
|
3000545c98 | ||
|
|
f3ad092ef2 | ||
|
|
a012e7df2f | ||
|
|
1fa77412c0 | ||
|
|
e56caeb22b | ||
|
|
ceb07ebc67 | ||
|
|
237075d45c | ||
|
|
7099b1946d | ||
|
|
e416ab8ff2 | ||
|
|
2802ed03a5 | ||
|
|
4af73c76fe | ||
|
|
b6149979aa | ||
|
|
cb20288427 | ||
|
|
db6f4c91dc | ||
|
|
57ac7a5277 | ||
|
|
136438f9c2 | ||
|
|
e2be8b3440 | ||
|
|
04f3473ef3 | ||
|
|
255cb0da8d | ||
|
|
069352fb0f | ||
|
|
66e8ddb684 | ||
|
|
179c26975c | ||
|
|
e17f6e799b | ||
|
|
7cd8294104 | ||
|
|
ede5540335 | ||
|
|
747279b773 | ||
|
|
44f8b1c477 | ||
|
|
7c6ebd9bf6 | ||
|
|
430469b708 | ||
|
|
efbb3cee31 | ||
|
|
21094ed4dd | ||
|
|
5f326efc7e | ||
|
|
b6e34ace35 | ||
|
|
fe4a8965e3 | ||
|
|
23371c233d | ||
|
|
7a3bbf0d7f | ||
|
|
89a1bec9c1 | ||
|
|
1c1e70619a | ||
|
|
0ff4374efa | ||
|
|
18d0e58a48 | ||
|
|
84f44338dc | ||
|
|
2ba0412890 | ||
|
|
2326522b29 | ||
|
|
a7cb6ee434 | ||
|
|
2aeef63565 | ||
|
|
3c9e7335ef | ||
|
|
49067de325 | ||
|
|
471e7e29ae | ||
|
|
3ec5775406 | ||
|
|
e804d2b60d | ||
|
|
742438a95d | ||
|
|
5c60086baa | ||
|
|
e49041bb14 | ||
|
|
f3cbe91883 | ||
|
|
ea439a2176 | ||
|
|
56e1e76f11 | ||
|
|
634e7357be | ||
|
|
08dc88da1a | ||
|
|
3d206e445c | ||
|
|
64686cdad1 | ||
|
|
d7fe09bdf1 | ||
|
|
6da50da92f | ||
|
|
51e4dd986f | ||
|
|
bee6522182 | ||
|
|
1711a9dd33 | ||
|
|
3914626379 | ||
|
|
df276cb32d | ||
|
|
daad7d8b10 | ||
|
|
3bf5bc0fe3 | ||
|
|
96abae553a | ||
|
|
f9cbfb1562 | ||
|
|
8eaa94e179 | ||
|
|
4f876b648b | ||
|
|
cd738137c0 | ||
|
|
5605eb129d | ||
|
|
87ef0f21a3 | ||
|
|
a1c7ce827e | ||
|
|
97466bcdfb | ||
|
|
ff3096b106 | ||
|
|
98f0d77f3f | ||
|
|
92548ba402 | ||
|
|
c46741d311 | ||
|
|
7c7c1abf7c | ||
|
|
fc303b1b0a | ||
|
|
4e220a9679 | ||
|
|
b17b1f7504 | ||
|
|
7081fc0e76 | ||
|
|
68e4574f19 | ||
|
|
e6e0a70012 | ||
|
|
13e38da942 | ||
|
|
468c1de26b | ||
|
|
22ef5ac0e5 | ||
|
|
ef2dc08958 | ||
|
|
6b84ffa16c | ||
|
|
d7a1096413 | ||
|
|
93b94a8bc2 | ||
|
|
9a95716105 | ||
|
|
dbfcf5d87a | ||
|
|
105d7d53b3 | ||
|
|
01cefe1457 | ||
|
|
7fe3db8017 | ||
|
|
e0945fac80 | ||
|
|
40fa190820 | ||
|
|
670580f8f3 | ||
|
|
323a0bcf16 | ||
|
|
6e995edd80 | ||
|
|
8d86e45b7a | ||
|
|
2aa6df4461 | ||
|
|
cf6f989502 | ||
|
|
3e1d8ae334 | ||
|
|
bcfe9484b5 | ||
|
|
5e4d1b9cfd | ||
|
|
3b463e7305 | ||
|
|
eedf5082fa | ||
|
|
2ea5b15175 | ||
|
|
7a9808aad3 | ||
|
|
a1d712694c | ||
|
|
ca11c726a3 | ||
|
|
6e0f7a35bd | ||
|
|
7375b001ca | ||
|
|
0287086633 | ||
|
|
9eb2becbb5 | ||
|
|
12f1444fe7 | ||
|
|
d6372bd093 | ||
|
|
f4d8ead54e | ||
|
|
7427e13505 | ||
|
|
445683c3d5 | ||
|
|
677c46c48a | ||
|
|
87b9e3f87a | ||
|
|
da2a5aff2f | ||
|
|
65d77743dc | ||
|
|
9f4bf13cc9 | ||
|
|
51b86f88b9 | ||
|
|
8184461b48 | ||
|
|
ca0cdd6e15 | ||
|
|
036a17ad3b | ||
|
|
2418023ddd |
@@ -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
|
||||||
|
|||||||
@@ -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.4.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,9 +62,8 @@ 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.2
|
rev: 2.7.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: editorconfig-checker
|
- id: editorconfig-checker
|
||||||
exclude: |
|
exclude: |
|
||||||
@@ -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.10.1
|
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: markdownlint
|
||||||
args: [ --py38-plus ]
|
args:
|
||||||
|
- --disable=MD013
|
||||||
- repo: https://github.com/adamchainz/django-upgrade
|
# Infrastructure
|
||||||
rev: 1.14.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.3.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."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
10
.tx/config
10
.tx/config
@@ -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
|
|
||||||
@@ -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.0b2'
|
__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__}'
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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' %}">
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{% load theme_tags %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
|
|
||||||
<title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title>
|
<title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title>
|
||||||
|
|
||||||
{% include 'bundles/bootstrap-css-bs5.html' %}
|
{% theme_css %}
|
||||||
{% include 'bundles/fontawesome.html' %}
|
{% include 'bundles/fontawesome.html' %}
|
||||||
|
|
||||||
{% block extra_include %}
|
{% block extra_include %}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-4">
|
<div class="col-md-6">
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
|
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-4">
|
<div class="col-md-6">
|
||||||
<div class="card card-login border-secondary p-3">
|
<div class="card card-login border-secondary p-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
|
|||||||
186
allianceauth/checks.py
Normal file
186
allianceauth/checks.py
Normal 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -92,6 +92,9 @@
|
|||||||
<a href="{% url 'groupmanagement:accept_request' acceptrequest.id %}" class="btn btn-success">
|
<a href="{% url 'groupmanagement:accept_request' acceptrequest.id %}" class="btn btn-success">
|
||||||
{% translate "Accept" %}
|
{% translate "Accept" %}
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{% url 'groupmanagement:reject_request' acceptrequest.id %}" class="btn btn-danger">
|
||||||
|
{% translate "Reject" %}
|
||||||
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
BIN
allianceauth/locale/cs/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/cs/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2776
allianceauth/locale/cs/LC_MESSAGES/django.po
Normal file
2776
allianceauth/locale/cs/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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-02-17 20:02+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"
|
||||||
@@ -26,6 +26,15 @@ msgstr ""
|
|||||||
msgid "Google Analytics V4"
|
msgid "Google Analytics V4"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/authentication/constants.py:6
|
||||||
|
msgid ""
|
||||||
|
"This software has exceeded the error limit for ESI. If you are a user, "
|
||||||
|
"please contact the maintainer of this software. If you are a developer/"
|
||||||
|
"maintainer, please make a greater effort in the future to receive valid "
|
||||||
|
"responses. For tips on how, come have a chat with us in ##3rd-party-dev-and-"
|
||||||
|
"esi on the EVE Online Discord. https://www.eveonline.com/discord"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/decorators.py:52
|
#: allianceauth/authentication/decorators.py:52
|
||||||
msgid "A main character is required to perform that action. Add one below."
|
msgid "A main character is required to perform that action. Add one below."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -104,27 +113,34 @@ msgstr ""
|
|||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:7
|
#: allianceauth/authentication/templates/authentication/dashboard.html:5
|
||||||
|
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||||
|
#: allianceauth/menu/templates/menu/sortable-side-menu.html:14
|
||||||
|
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||||
|
msgid "Dashboard"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:7
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:33
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:33
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:54
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:54
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:13
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:13
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:14
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:14
|
||||||
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:4
|
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:4
|
||||||
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:6
|
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:6
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:16
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:16
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:17
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:17
|
||||||
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:10
|
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:10
|
||||||
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:12
|
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:12
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:24
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:24
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:89
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:89
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:23
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:23
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:31
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:31
|
||||||
@@ -133,32 +149,25 @@ msgstr ""
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:25
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:33
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:33
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.characters.html:26
|
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:26
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:125
|
#: allianceauth/corputils/templates/corputils/corpstats.html:125
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:63
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:63
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.groups.html:5
|
#: allianceauth/authentication/templates/authentication/dashboard_groups.html:5
|
||||||
msgid "Membership"
|
msgid "Membership"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.groups.html:8
|
#: allianceauth/authentication/templates/authentication/dashboard_groups.html:8
|
||||||
msgid "State:"
|
msgid "State:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:5
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
|
||||||
#: allianceauth/menu/templates/menu/sortable-side-menu.html:14
|
|
||||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
|
||||||
msgid "Dashboard"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/tokens.html:6
|
#: allianceauth/authentication/templates/authentication/tokens.html:6
|
||||||
#: allianceauth/authentication/templates/authentication/tokens.html:10
|
#: allianceauth/authentication/templates/authentication/tokens.html:10
|
||||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||||
@@ -197,7 +206,7 @@ msgstr ""
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:29
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:29
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:28
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:28
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:115
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:118
|
||||||
msgid "Character"
|
msgid "Character"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -234,49 +243,49 @@ msgstr ""
|
|||||||
msgid "Invalid or expired activation link."
|
msgid "Invalid or expired activation link."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:147
|
#: allianceauth/authentication/views.py:158
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Cannot change main character to %(char)s: character owned by a different "
|
"Cannot change main character to %(char)s: character owned by a different "
|
||||||
"account."
|
"account."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:153
|
#: allianceauth/authentication/views.py:165
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Changed main character to %(char)s"
|
msgid "Changed main character to %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:162
|
#: allianceauth/authentication/views.py:179
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added %(name)s to your account."
|
msgid "Added %(name)s to your account."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:164
|
#: allianceauth/authentication/views.py:181
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:207
|
#: allianceauth/authentication/views.py:226
|
||||||
msgid ""
|
msgid ""
|
||||||
"Unable to authenticate as the selected character. Please log in with the "
|
"Unable to authenticate as the selected character. Please log in with the "
|
||||||
"main character associated with this account."
|
"main character associated with this account."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:273
|
#: allianceauth/authentication/views.py:293
|
||||||
msgid "Registration token has expired."
|
msgid "Registration token has expired."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:331
|
#: allianceauth/authentication/views.py:354
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sent confirmation email. Please follow the link to confirm your email "
|
"Sent confirmation email. Please follow the link to confirm your email "
|
||||||
"address."
|
"address."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:336
|
#: allianceauth/authentication/views.py:360
|
||||||
msgid "Confirmed your email address. Please login to continue."
|
msgid "Confirmed your email address. Please login to continue."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/views.py:341
|
#: allianceauth/authentication/views.py:366
|
||||||
msgid "Registration of new accounts is not allowed at this time."
|
msgid "Registration of new accounts is not allowed at this time."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -495,7 +504,6 @@ msgstr ""
|
|||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:44
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:44
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:92
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:92
|
||||||
#: allianceauth/templates/allianceauth/top-menu.html:23
|
#: allianceauth/templates/allianceauth/top-menu.html:23
|
||||||
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/timertable.html:11
|
#: allianceauth/timerboard/templates/timerboard/timertable.html:11
|
||||||
msgid "Eve Time"
|
msgid "Eve Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -833,7 +841,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:56
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:56
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:116
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:119
|
||||||
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:32
|
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:32
|
||||||
msgid "Organization"
|
msgid "Organization"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -845,7 +853,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:60
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:60
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:85
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:85
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:145
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:148
|
||||||
#: allianceauth/permissions_tool/templates/permissions_tool/audit_row.html:18
|
#: allianceauth/permissions_tool/templates/permissions_tool/audit_row.html:18
|
||||||
msgid "(unknown)"
|
msgid "(unknown)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -926,7 +934,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:36
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:36
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:57
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:57
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:117
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:120
|
||||||
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:29
|
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:29
|
||||||
#: allianceauth/services/modules/openfire/forms.py:6
|
#: allianceauth/services/modules/openfire/forms.py:6
|
||||||
msgid "Group"
|
msgid "Group"
|
||||||
@@ -980,20 +988,21 @@ msgid "Group Membership"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:93
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:93
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:153
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:156
|
||||||
msgid "Accept"
|
msgid "Accept"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:103
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:96
|
||||||
msgid "No group add requests."
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:160
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:157
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:104
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:104
|
||||||
msgid "Reject"
|
msgid "Reject"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:166
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:106
|
||||||
|
msgid "No group add requests."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:169
|
||||||
msgid "No group leave requests."
|
msgid "No group leave requests."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1376,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 ""
|
||||||
|
|
||||||
@@ -1458,7 +1469,6 @@ msgid "Doctrine"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:14
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:17
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1523,6 +1533,11 @@ msgstr ""
|
|||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:17
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:17
|
||||||
|
msgid "EVE Time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/timertable.html:12
|
#: allianceauth/timerboard/templates/timerboard/timertable.html:12
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
@@ -1568,17 +1583,17 @@ msgstr ""
|
|||||||
msgid "Update fleet operation"
|
msgid "Update fleet operation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:89
|
#: allianceauth/optimer/views.py:91
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:118
|
#: allianceauth/optimer/views.py:120
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:169
|
#: allianceauth/optimer/views.py:171
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2367,6 +2382,10 @@ msgstr ""
|
|||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/templates/allianceauth/admin-status/esi_check.html:4
|
||||||
|
msgid "Your Server received an ESI error response code of "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:11
|
#: allianceauth/templates/allianceauth/admin-status/overview.html:11
|
||||||
msgid "Alliance Auth Notifications"
|
msgid "Alliance Auth Notifications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2693,12 +2712,12 @@ msgstr ""
|
|||||||
msgid "Past Timers"
|
msgid "Past Timers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:78
|
#: allianceauth/timerboard/views.py:85
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:87
|
#: allianceauth/timerboard/views.py:95
|
||||||
msgid "Saved changes to the timer."
|
msgid "Saved changes to the timer."
|
||||||
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
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
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
allianceauth/locale/nl/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/nl/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2789
allianceauth/locale/nl/LC_MESSAGES/django.po
Normal file
2789
allianceauth/locale/nl/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
allianceauth/locale/pl_PL/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/pl_PL/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2848
allianceauth/locale/pl_PL/LC_MESSAGES/django.po
Normal file
2848
allianceauth/locale/pl_PL/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
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
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||||
|
|||||||
@@ -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 = ""
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{% endif %}>
|
{% endif %}>
|
||||||
</i>
|
</i>
|
||||||
<a
|
<a
|
||||||
class="nav-link flex-fill align-self-center me-auto"
|
class="nav-link flex-fill align-self-center me-auto {% if item.navactive %}{% navactive request item.navactive|join:' ' %}{% endif %}"
|
||||||
{% if item.is_folder %}
|
{% if item.is_folder %}
|
||||||
type="button"
|
type="button"
|
||||||
data-bs-toggle="collapse"
|
data-bs-toggle="collapse"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<ul id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2">
|
<ul id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2">
|
||||||
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">
|
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">
|
||||||
<i class="nav-link fas fa-tachometer-alt fa-fw align-self-center me-3 {% navactive request 'authentication:dashboard' %}"></i>
|
<i class="nav-link fas fa-tachometer-alt fa-fw align-self-center me-3 {% navactive request 'authentication:dashboard' %}"></i>
|
||||||
<a class="nav-link flex-fill align-self-center" href="{% url 'authentication:dashboard' %}">
|
<a class="nav-link flex-fill align-self-center {% navactive request 'authentication:dashboard' %}" href="{% url 'authentication:dashboard' %}">
|
||||||
{% translate "Dashboard" %}
|
{% translate "Dashboard" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
@@ -98,6 +98,10 @@ def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
|
|||||||
|
|
||||||
if item.is_app_item:
|
if item.is_app_item:
|
||||||
rendered_item = _render_app_item(request, hook_items, item, bs5_template)
|
rendered_item = _render_app_item(request, hook_items, item, bs5_template)
|
||||||
|
if rendered_item.html == "":
|
||||||
|
# If there is no content dont render it.
|
||||||
|
# This item has probably been hidden by permissions
|
||||||
|
continue
|
||||||
elif item.is_link_item:
|
elif item.is_link_item:
|
||||||
rendered_item = _render_link_item(request, item, bs5_template)
|
rendered_item = _render_link_item(request, item, bs5_template)
|
||||||
elif item.is_folder:
|
elif item.is_folder:
|
||||||
@@ -127,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()
|
||||||
@@ -157,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:
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ def create_user(permissions=None, **kwargs) -> User:
|
|||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def create_menu_item_hook_class(**kwargs) -> MenuItemHook:
|
||||||
|
num = next(counter_menu_item_hook)
|
||||||
|
return type(f"GeneratedMenuItem{num}", (MenuItemHook,), {})
|
||||||
|
|
||||||
|
|
||||||
def create_menu_item_hook(**kwargs) -> MenuItemHook:
|
def create_menu_item_hook(**kwargs) -> MenuItemHook:
|
||||||
num = next(counter_menu_item_hook)
|
num = next(counter_menu_item_hook)
|
||||||
new_class = type(f"GeneratedMenuItem{num}", (MenuItemHook,), {})
|
new_class = type(f"GeneratedMenuItem{num}", (MenuItemHook,), {})
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from allianceauth.menu.tests.factories import (
|
|||||||
create_folder_menu_item,
|
create_folder_menu_item,
|
||||||
create_link_menu_item,
|
create_link_menu_item,
|
||||||
create_menu_item_from_hook,
|
create_menu_item_from_hook,
|
||||||
|
create_menu_item_hook_class,
|
||||||
create_menu_item_hook_function,
|
create_menu_item_hook_function,
|
||||||
create_rendered_menu_item,
|
create_rendered_menu_item,
|
||||||
)
|
)
|
||||||
@@ -177,6 +178,44 @@ class TestRenderDefaultMenu(TestCase):
|
|||||||
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
||||||
self.assertEqual(menu[1].menu_item.text, "Bravo")
|
self.assertEqual(menu[1].menu_item.text, "Bravo")
|
||||||
|
|
||||||
|
def test_should_remove_empty_folders_with_items_hidden(self, mock_get_hooks):
|
||||||
|
# given
|
||||||
|
|
||||||
|
class TestHook(create_menu_item_hook_class()):
|
||||||
|
text = "Dummy App No Data"
|
||||||
|
classes = "fa-solid fa-users-gear"
|
||||||
|
url_name = "groupmanagement:management"
|
||||||
|
|
||||||
|
def render(Self, request):
|
||||||
|
# simulate no perms
|
||||||
|
return ""
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"text": "Alpha",
|
||||||
|
"classes": "fa-solid fa-users-gear",
|
||||||
|
"url_name": "groupmanagement:management",
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha = TestHook(**params)
|
||||||
|
|
||||||
|
hooks = [lambda: alpha]
|
||||||
|
|
||||||
|
mock_get_hooks.return_value = hooks
|
||||||
|
|
||||||
|
folder = create_folder_menu_item(text="Folder", order=2)
|
||||||
|
create_menu_item_from_hook(hooks[0], parent=folder)
|
||||||
|
create_link_menu_item(text="Bravo", order=3) # this is all that should show
|
||||||
|
|
||||||
|
request = self.factory.get("/")
|
||||||
|
|
||||||
|
# when
|
||||||
|
result = render_menu(request)
|
||||||
|
|
||||||
|
# then
|
||||||
|
menu = list(result)
|
||||||
|
self.assertEqual(len(menu), 1)
|
||||||
|
self.assertEqual(menu[0].menu_item.text, "Bravo")
|
||||||
|
|
||||||
def test_should_not_include_hidden_items(self, mock_get_hooks):
|
def test_should_not_include_hidden_items(self, mock_get_hooks):
|
||||||
# given
|
# given
|
||||||
mock_get_hooks.return_value = []
|
mock_get_hooks.return_value = []
|
||||||
@@ -196,7 +235,6 @@ class TestRenderDefaultMenu(TestCase):
|
|||||||
self.assertEqual(menu[1].menu_item.text, "Charlie")
|
self.assertEqual(menu[1].menu_item.text, "Charlie")
|
||||||
|
|
||||||
def test_should_not_render_hidden_folders(self, mock_get_hooks):
|
def test_should_not_render_hidden_folders(self, mock_get_hooks):
|
||||||
# given
|
|
||||||
# given
|
# given
|
||||||
menu = [
|
menu = [
|
||||||
create_menu_item_hook_function(text="Charlie", count=42),
|
create_menu_item_hook_function(text="Charlie", count=42),
|
||||||
@@ -309,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:
|
||||||
|
|||||||
@@ -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 style="height: 300px; overflow-y:auto;">
|
<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 "Start 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>
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ from .models import OpTimer, OpTimerType
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
OPS_VIEW_PERMISSION = 'auth.optimer_view'
|
||||||
|
OPS_MANAGE_PERMISSION = 'auth.optimer_management'
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('auth.optimer_view')
|
@permission_required(OPS_VIEW_PERMISSION)
|
||||||
def optimer_view(request):
|
def optimer_view(request):
|
||||||
"""
|
"""
|
||||||
View for the optimer management page
|
View for the optimer management page
|
||||||
@@ -39,7 +41,7 @@ def optimer_view(request):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('auth.optimer_management')
|
@permission_required(OPS_MANAGE_PERMISSION)
|
||||||
def add_optimer_view(request):
|
def add_optimer_view(request):
|
||||||
"""
|
"""
|
||||||
View for the add optimer page
|
View for the add optimer page
|
||||||
@@ -98,7 +100,7 @@ def add_optimer_view(request):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('auth.optimer_management')
|
@permission_required(OPS_MANAGE_PERMISSION)
|
||||||
def remove_optimer(request, optimer_id):
|
def remove_optimer(request, optimer_id):
|
||||||
"""
|
"""
|
||||||
Remove optimer
|
Remove optimer
|
||||||
@@ -121,7 +123,7 @@ def remove_optimer(request, optimer_id):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('auth.optimer_management')
|
@permission_required(OPS_MANAGE_PERMISSION)
|
||||||
def edit_optimer(request, optimer_id):
|
def edit_optimer(request, optimer_id):
|
||||||
"""
|
"""
|
||||||
Edit optimer
|
Edit optimer
|
||||||
@@ -192,14 +194,22 @@ def dashboard_ops(request):
|
|||||||
:return:
|
:return:
|
||||||
:rtype:
|
:rtype:
|
||||||
"""
|
"""
|
||||||
|
if request.user.has_perm(OPS_VIEW_PERMISSION):
|
||||||
|
base_query = OpTimer.objects.select_related('eve_character', 'type')
|
||||||
|
timers = base_query.filter(
|
||||||
|
start__gte=timezone.now()
|
||||||
|
)[:5]
|
||||||
|
|
||||||
base_query = OpTimer.objects.select_related('eve_character', 'type')
|
if timers.count():
|
||||||
timers = base_query.filter(start__gte=timezone.now())[:5]
|
context = {
|
||||||
|
'timers': timers,
|
||||||
if timers.count():
|
}
|
||||||
context = {
|
return render_to_string(
|
||||||
'timers': timers,
|
'optimer/dashboard.ops.html',
|
||||||
}
|
context=context,
|
||||||
return render_to_string('optimer/dashboard.ops.html', context=context, request=request)
|
request=request
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ LANGUAGES = (
|
|||||||
("ja", "Japanese"),
|
("ja", "Japanese"),
|
||||||
("it", "Italian"),
|
("it", "Italian"),
|
||||||
("uk", "Ukrainian"),
|
("uk", "Ukrainian"),
|
||||||
|
("pl", "Polish"),
|
||||||
)
|
)
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
|
|||||||
@@ -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 [])
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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, {
|
||||||
|
|||||||
@@ -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,25 +46,24 @@
|
|||||||
</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 w-100 cursor-default">
|
<div class="btn h-100 w-100 cursor-default">
|
||||||
<h5 class="list-group-item-heading">{% translate "Current" %}</h5>
|
<h5 class="list-group-item-heading">{% translate "Current" %}</h5>
|
||||||
<p class="list-group-item-text">{{ current_version }}</p>
|
<p class="list-group-item-text">{{ current_version }}</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="list-group-item bg-{% if latest_patch %}success{% elif latest_minor %}warning{% else %}danger{% endif %} w-100">
|
<li class="list-group-item bg-{% if latest_patch %}success{% elif latest_minor %}warning{% else %}danger{% endif %} w-100">
|
||||||
<a class="btn w-100" href="https://gitlab.com/allianceauth/allianceauth/-/releases/v{{ latest_patch_version }}">
|
<a class="btn h-100 w-100" href="https://gitlab.com/allianceauth/allianceauth/-/releases/v{{ latest_patch_version }}">
|
||||||
<h5 class="list-group-item-heading">{% translate "Latest Stable" %}</h5>
|
<h5 class="list-group-item-heading">{% translate "Latest Stable" %}</h5>
|
||||||
|
|
||||||
<p class="list-group-item-text">
|
<p class="list-group-item-text">
|
||||||
@@ -82,7 +76,7 @@
|
|||||||
|
|
||||||
{% if latest_beta %}
|
{% if latest_beta %}
|
||||||
<li class="list-group-item bg-info w-100">
|
<li class="list-group-item bg-info w-100">
|
||||||
<a class="btn w-100" href="https://gitlab.com/allianceauth/allianceauth/-/releases/v{{ latest_beta_version }}">
|
<a class="btn h-100 w-100" href="https://gitlab.com/allianceauth/allianceauth/-/releases/v{{ latest_beta_version }}">
|
||||||
<h5 class="list-group-item-heading">{% translate "Latest Pre-Release" %}</h5>
|
<h5 class="list-group-item-heading">{% translate "Latest Pre-Release" %}</h5>
|
||||||
|
|
||||||
<p class="list-group-item-text">
|
<p class="list-group-item-text">
|
||||||
@@ -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 }}
|
||||||
|
|||||||
@@ -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 -->
|
||||||
@@ -98,23 +95,27 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
(() => {
|
(() => {
|
||||||
// TODO Extend this to the groups in the sidebar too.
|
|
||||||
// TODO Move to own JS file
|
// TODO Move to own JS file
|
||||||
const sidebar = document.getElementById('sidebar');
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
|
||||||
sidebar.addEventListener("shown.bs.collapse", () => {
|
sidebar.addEventListener('shown.bs.collapse', () => {
|
||||||
localStorage.removeItem("sidebar_" + sidebar.id);
|
localStorage.removeItem('sidebar_' + sidebar.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
sidebar.addEventListener("hidden.bs.collapse", () => {
|
sidebar.addEventListener('hidden.bs.collapse', () => {
|
||||||
localStorage.setItem("sidebar_" + sidebar.id, true);
|
localStorage.setItem('sidebar_' + sidebar.id, 'closed');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (localStorage.getItem("sidebar_" + sidebar.id) === "true") {
|
if (localStorage.getItem('sidebar_' + sidebar.id) === 'closed') {
|
||||||
sidebar.classList.remove("show")
|
sidebar.classList.remove('show')
|
||||||
} else {
|
} else {
|
||||||
sidebar.classList.add("show")
|
sidebar.classList.add("show")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeChildMenuItem = document.querySelector('#sidebar-menu li ul li a.active');
|
||||||
|
if (activeChildMenuItem) {
|
||||||
|
activeChildMenuItem.parentElement.parentElement.classList.add('show');
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.apps import AppConfig
|
|||||||
class BootstrapThemeConfig(AppConfig):
|
class BootstrapThemeConfig(AppConfig):
|
||||||
name = "allianceauth.theme.bootstrap"
|
name = "allianceauth.theme.bootstrap"
|
||||||
label = "bootstrap"
|
label = "bootstrap"
|
||||||
version = "5.3.0"
|
version = "5.3.3"
|
||||||
verbose_name = f"Bootstrap v{version}"
|
verbose_name = f"Bootstrap v{version}"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ from allianceauth.theme.hooks import ThemeHook
|
|||||||
|
|
||||||
|
|
||||||
CSS_STATICS = [{
|
CSS_STATICS = [{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css",
|
||||||
"integrity": "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
|
"integrity": "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg=="
|
||||||
}]
|
}]
|
||||||
|
|
||||||
JS_STATICS = [{
|
JS_STATICS = [{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
||||||
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
||||||
}, {
|
}, {
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js",
|
||||||
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
|
"integrity": "sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ=="
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.apps import AppConfig
|
|||||||
class DarklyThemeConfig(AppConfig):
|
class DarklyThemeConfig(AppConfig):
|
||||||
name = "allianceauth.theme.darkly"
|
name = "allianceauth.theme.darkly"
|
||||||
label = "darkly"
|
label = "darkly"
|
||||||
version = "5.3.0"
|
version = "5.3.3"
|
||||||
verbose_name = f"Bootswatch Darkly v{version}"
|
verbose_name = f"Bootswatch Darkly v{version}"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ class DarklyThemeHook(ThemeHook):
|
|||||||
"Darkly",
|
"Darkly",
|
||||||
"Flatly in night mode!",
|
"Flatly in night mode!",
|
||||||
css=[{
|
css=[{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.2.3/darkly/bootstrap.min.css",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/darkly/bootstrap.min.css",
|
||||||
"integrity": "sha512-YRcmztDXzJQCCBk2YUiEAY+r74gu/c9UULMPTeLsAp/Tw5eXiGkYMPC4tc4Kp1jx/V9xjEOCVpBe4r6Lx6n5dA=="
|
"integrity": "sha512-HDszXqSUU0om4Yj5dZOUNmtwXGWDa5ppESlX98yzbBS+z+3HQ8a/7kcdI1dv+jKq+1V5b01eYurE7+yFjw6Rdg=="
|
||||||
}],
|
}],
|
||||||
js=[{
|
js=[{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
||||||
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
||||||
}, {
|
}, {
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/js/bootstrap.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js",
|
||||||
"integrity": "sha512-1/RvZTcCDEUjY/CypiMz+iqqtaoQfAITmNSJY17Myp4Ms5mdxPS5UV7iOfdZoxcGhzFbOm6sntTKJppjvuhg4g=="
|
"integrity": "sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ=="
|
||||||
}],
|
}],
|
||||||
header_padding="4.5em"
|
header_padding="4.5em"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.apps import AppConfig
|
|||||||
class FlatlyThemeConfig(AppConfig):
|
class FlatlyThemeConfig(AppConfig):
|
||||||
name = "allianceauth.theme.flatly"
|
name = "allianceauth.theme.flatly"
|
||||||
label = "flatly"
|
label = "flatly"
|
||||||
version = "5.3.0"
|
version = "5.3.3"
|
||||||
verbose_name = f"Bootswatch Flatly v{version}"
|
verbose_name = f"Bootswatch Flatly v{version}"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ class FlatlyThemeHook(ThemeHook):
|
|||||||
"Flatly",
|
"Flatly",
|
||||||
"Flat and modern!",
|
"Flat and modern!",
|
||||||
css=[{
|
css=[{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.2/flatly/bootstrap.min.css",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/flatly/bootstrap.min.css",
|
||||||
"integrity": "sha512-rx+BMEjKes84XHg1erhvtq7Mqxm/lm6w4WMoCtDAaTMtUzT5iK5hNTu8mc2+yPNSldAX5hheN/ZhtNQjjYy5nA=="
|
"integrity": "sha512-qoT4KwnRpAQ9uczPsw7GunsNmhRnYwSlE2KRCUPRQHSkDuLulCtDXuC2P/P6oqr3M5hoGagUG9pgHDPkD2zCDA=="
|
||||||
}],
|
}],
|
||||||
js=[{
|
js=[{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
||||||
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
||||||
}, {
|
}, {
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js",
|
||||||
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
|
"integrity": "sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ=="
|
||||||
}],
|
}],
|
||||||
header_padding="4.5em"
|
header_padding="4.5em"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.apps import AppConfig
|
|||||||
class MateriaThemeConfig(AppConfig):
|
class MateriaThemeConfig(AppConfig):
|
||||||
name = "allianceauth.theme.materia"
|
name = "allianceauth.theme.materia"
|
||||||
label = "materia"
|
label = "materia"
|
||||||
version = "5.3.0"
|
version = "5.3.3"
|
||||||
verbose_name = f"Bootswatch Materia v{version}"
|
verbose_name = f"Bootswatch Materia v{version}"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ class MateriaThemeHook(ThemeHook):
|
|||||||
"Materia",
|
"Materia",
|
||||||
"Material is the metaphor",
|
"Material is the metaphor",
|
||||||
css=[{
|
css=[{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.2/materia/bootstrap.min.css",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/materia/bootstrap.min.css",
|
||||||
"integrity": "sha512-4+PCWoNUxEeasuW2ipP8Avsr7X/oS61Kz2CLdwS6ZfHt7jLuzAAcIfPlWLg4aGaDNo0GSmTOmM9biaqnmo6P7g=="
|
"integrity": "sha512-2S9Do+uTmZmmJpdmAcOKdUrK/YslcvAuRfIF2ws8+BW9AvZXMRZM+o8Wq+PZrfISD6ZlIaeCWWZAdeprXIoYuQ=="
|
||||||
}],
|
}],
|
||||||
js=[{
|
js=[{
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
||||||
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
||||||
}, {
|
}, {
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js",
|
||||||
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
|
"integrity": "sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ=="
|
||||||
}],
|
}],
|
||||||
header_padding="5.25em"
|
header_padding="5.25em"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 style="height: 300px; overflow-y:auto;">
|
|
||||||
<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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ from allianceauth.timerboard.models import Timer
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
TIMER_VIEW_PERMISSION = 'auth.timer_view'
|
||||||
|
TIMER_MANAGE_PERMISSION = 'auth.timer_management'
|
||||||
|
|
||||||
|
|
||||||
class BaseTimerView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
class BaseTimerView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
pass
|
pass
|
||||||
@@ -27,7 +30,7 @@ class BaseTimerView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
|||||||
|
|
||||||
class TimerView(BaseTimerView):
|
class TimerView(BaseTimerView):
|
||||||
template_name = 'timerboard/view.html'
|
template_name = 'timerboard/view.html'
|
||||||
permission_required = 'auth.timer_view'
|
permission_required = TIMER_VIEW_PERMISSION
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
logger.debug(f"timer_view called by user {request.user}")
|
logger.debug(f"timer_view called by user {request.user}")
|
||||||
@@ -48,7 +51,7 @@ class TimerView(BaseTimerView):
|
|||||||
|
|
||||||
|
|
||||||
class TimerManagementView(BaseTimerView):
|
class TimerManagementView(BaseTimerView):
|
||||||
permission_required = 'auth.timer_management'
|
permission_required = TIMER_MANAGE_PERMISSION
|
||||||
index_redirect = 'timerboard:view'
|
index_redirect = 'timerboard:view'
|
||||||
success_url = reverse_lazy(index_redirect)
|
success_url = reverse_lazy(index_redirect)
|
||||||
model = Timer
|
model = Timer
|
||||||
@@ -74,8 +77,13 @@ class AddTimerView(TimerManagementView, AddUpdateMixin, CreateView):
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
result = super().form_valid(form)
|
result = super().form_valid(form)
|
||||||
timer = self.object
|
timer = self.object
|
||||||
logger.info(f"Created new timer in {timer.system} at {timer.eve_time} by user {self.request.user}")
|
logger.info(
|
||||||
messages.success(self.request, _('Added new timer in %(system)s at %(time)s.') % {"system": timer.system, "time": timer.eve_time})
|
f"Created new timer in {timer.system} at {timer.eve_time} by user {self.request.user}"
|
||||||
|
)
|
||||||
|
messages.success(
|
||||||
|
self.request,
|
||||||
|
_('Added new timer in %(system)s at %(time)s.') % {"system": timer.system, "time": timer.eve_time}
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -89,22 +97,33 @@ 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):
|
||||||
try:
|
if request.user.has_perm(TIMER_VIEW_PERMISSION):
|
||||||
corp = request.user.profile.main_character.corporation
|
try:
|
||||||
except (EveCorporationInfo.DoesNotExist, AttributeError):
|
corp = request.user.profile.main_character.corporation
|
||||||
return ""
|
except (EveCorporationInfo.DoesNotExist, AttributeError):
|
||||||
|
return ""
|
||||||
|
|
||||||
timers = Timer.objects.select_related('eve_character').filter((Q(eve_corp__isnull=True) | Q(eve_corp=corp)) ,eve_time__gte=timezone.now())[:5]
|
timers = Timer.objects.select_related(
|
||||||
|
'eve_character'
|
||||||
|
).filter(
|
||||||
|
(Q(corp_timer=True) & Q(eve_corp=corp)) | Q(corp_timer=False),
|
||||||
|
eve_time__gte=timezone.now()
|
||||||
|
)[:5]
|
||||||
|
|
||||||
if timers.count():
|
if timers.count():
|
||||||
context = {
|
context = {
|
||||||
'timers': timers,
|
'timers': timers,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render_to_string(template_name='timerboard/dashboard.timers.html', context=context, request=request)
|
return render_to_string(
|
||||||
|
template_name='timerboard/dashboard.timers.html',
|
||||||
|
context=context, request=request
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.0b2
|
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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
ARG AUTH_VERSION=v4.0.0b2
|
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
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user