Compare commits

..

85 Commits

Author SHA1 Message Date
Joel Falknau
34b94ae685
Version Bump 4.8.0 2025-07-03 09:19:25 +10:00
Ariel Rin
50fd900bdc Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1732
2025-07-02 23:17:15 +00:00
Ariel Rin
1bf8ec5bc6 Updates for project Alliance Auth 2025-07-02 23:17:15 +00:00
Ariel Rin
f849b75029 Merge branch 'fix-sidebar-localStorage-behavior' into 'master'
[FIX] Sidebar `localStorage` behavior

See merge request allianceauth/allianceauth!1733
2025-07-02 23:06:28 +00:00
Peter Pfeufer
25c27793fe
[FIX] Sidebar localStorage behavior 2025-07-01 14:07:12 +02:00
Ariel Rin
6e413772ad Merge branch 'custom-static-file-storage' into 'master'
[ADD] Custom Static Files Storage Class

See merge request allianceauth/allianceauth!1726
2025-06-30 23:43:33 +00:00
Ariel Rin
137a202e1b Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1725
2025-06-19 10:31:37 +00:00
Joel Falknau
aaf718fe4d
update pre-commit 2025-06-19 20:30:37 +10:00
Joel Falknau
a193d9959b
makemessages 2025-06-19 20:23:21 +10:00
Ariel Rin
12250ef0c2 Merge branch 'exclude-biomassed-characters' into 'master'
[ADD] `exclude_biomassed` to `EveCharacterManager`

See merge request allianceauth/allianceauth!1727
2025-06-19 10:13:47 +00:00
Ariel Rin
bde9802583 Merge branch 'user-menu-fixes' into 'master'
[CHANGE] User Menu Template Improvements

See merge request allianceauth/allianceauth!1728
2025-06-19 10:12:16 +00:00
Ariel Rin
1b30b86d2b Merge branch 'improve-get_all_characters_from_user' into 'master'
[CHANGE] Improve `get_all_characters_from_user`

See merge request allianceauth/allianceauth!1729
2025-06-19 10:11:43 +00:00
Ariel Rin
0707b9b98c Merge branch 'error-not-warning' into 'master'
[FIX] Error codes should trigger errors, not warnings

See merge request allianceauth/allianceauth!1730
2025-06-19 10:11:09 +00:00
Peter Pfeufer
b22a379db2
[FIX] Error codes should trigger errors, not warnings 2025-06-17 17:27:51 +02:00
Peter Pfeufer
bb2e0aabbc
[CHANGE] Improve get_all_characters_from_user
### Added

- `main_first` option to move the main character to the first position of the character list

### Changed

- Character list sorted alphabetically
2025-06-17 16:38:55 +02:00
Peter Pfeufer
449991d846
[FIX] Several HTML fixes
- Remove unnecessary HTML tags
- Remove unnecessary Bootstrap classes
- Add title attributes
- Make strings translatable
2025-06-15 12:15:14 +02:00
Peter Pfeufer
dd42c2b074
[CHANGE] Only show theme selection if more than 1 theme is available 2025-06-15 12:10:44 +02:00
Peter Pfeufer
abff1b0add
[ADD] exclude_biomassed to EveCharacterManager 2025-06-13 11:29:05 +02:00
Peter Pfeufer
fc51f6bea2
[FIX] Cleanup file path name to work with CSS url("foobar") notations
This essentially removes quotes from the filename, which aren't allowed anyways.
2025-06-12 10:19:48 +02:00
Peter Pfeufer
6477c22308
[CHANGE] Use the same quotation marks for strings and not a mix of both
Just while we're at it …
2025-06-01 00:05:34 +02:00
Peter Pfeufer
329b3fecfb
[ADD] Custom Static Files Storage Class 2025-06-01 00:02:09 +02:00
Ariel Rin
677505f22a Translate django.po in pl_PL
92% of minimum 50% translated source file: 'django.po'
on 'pl_PL'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:31:18 +00:00
Ariel Rin
f518166bd0 Translate django.po in uk
96% of minimum 50% translated source file: 'django.po'
on 'uk'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:31:11 +00:00
Ariel Rin
1f4c49f823 Translate django.po in ko_KR
92% of minimum 50% translated source file: 'django.po'
on 'ko_KR'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:31:00 +00:00
Ariel Rin
abcc4d47b5 Translate django.po in ru
76% of minimum 50% translated source file: 'django.po'
on 'ru'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:30:53 +00:00
Ariel Rin
3d4737df72 Translate django.po in it_IT
90% of minimum 50% translated source file: 'django.po'
on 'it_IT'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:30:47 +00:00
Ariel Rin
8f94885d8e Translate django.po in de
96% of minimum 50% translated source file: 'django.po'
on 'de'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:30:40 +00:00
Ariel Rin
993455d664 Translate django.po in fr_FR
92% of minimum 50% translated source file: 'django.po'
on 'fr_FR'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:30:33 +00:00
Ariel Rin
3cb0addee7 Translate django.po in ja
92% of minimum 50% translated source file: 'django.po'
on 'ja'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:30:26 +00:00
Ariel Rin
5530b76294 Translate django.po in es
80% of minimum 50% translated source file: 'django.po'
on 'es'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:30:16 +00:00
Ariel Rin
9fb51165ab Translate django.po in zh-Hans
67% of minimum 50% translated source file: 'django.po'
on 'zh-Hans'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-05-24 08:30:09 +00:00
Joel Falknau
a650f0730e
Version Bump 4.7.0 2025-05-24 17:28:41 +10:00
Ariel Rin
63eb9edc9c Merge branch 'readme' into 'master'
Feedback Wanted - Update Readme

See merge request allianceauth/allianceauth!1696
2025-05-24 06:23:06 +00:00
Ariel Rin
d6e1eb9792 Feedback Wanted - Update Readme 2025-05-24 06:23:06 +00:00
Ariel Rin
b459f96e6b Merge branch 'autogroup-multiple-configs' into 'master'
[Fix] Multiple AutoGroupsConfig will override each others

See merge request allianceauth/allianceauth!1722
2025-05-24 04:05:28 +00:00
Ariel Rin
bf32f2c1ef Merge branch 'fix-instance-must-be-saved' into 'master'
Fix discord test errror

See merge request allianceauth/allianceauth!1723
2025-05-24 04:04:27 +00:00
Ariel Rin
7ca67ebaae Merge branch 'fix-active-class' into 'master'
[FIX] Active class on notification icon

See merge request allianceauth/allianceauth!1724
2025-05-24 04:00:59 +00:00
Peter Pfeufer
fa32f87a35
[FIX] Active class on notification icon 2025-05-22 09:21:05 +02:00
T'rahk Rokym
a630015451 Fix error on one of my tox settings
ValueError: Model instances passed to related filters must be saved.
2025-05-21 21:52:40 +02:00
T'rahk Rokym
bf43f59232 Fix the role distribution if several configs hold the same corporation/alliance 2025-05-21 21:29:21 +02:00
Ariel Rin
54910746e3 Merge branch 'bootstrap-class-fixes' into 'master'
[FIX] BG color classes

See merge request allianceauth/allianceauth!1716
2025-05-06 02:03:56 +00:00
Ariel Rin
07ae68333d Merge branch 'improve-add-character-icon' into 'master'
[CHANGE] Better "Add Character" Icon

See merge request allianceauth/allianceauth!1720
2025-05-06 02:03:50 +00:00
Ariel Rin
69e70a4c9b Merge branch '1426-fix-srp-datatable-warning' into 'master'
[FIX] SRP Datatable Warning about incorrect column count

Closes #1426

See merge request allianceauth/allianceauth!1721
2025-05-06 02:03:44 +00:00
Peter Pfeufer
b55f11ee74
[FIX] SRP Datatable Warning about incorrect column count
This was triggered by an incorrect column count due to missing permissions, or not taking missing permissions into account when loading the page and setting up the non-orderable columns.

Fixes #1426
2025-05-05 01:49:40 +02:00
Peter Pfeufer
94ee3c0203
[CHANGE] Better "Add Character" Icon 2025-05-02 01:06:26 +02:00
Peter Pfeufer
25cf329a50
[FIX] BG color classes
Use `text-bg-*` instead of just `bg-*` to make use of Bootstraps native text color selection for those backgrounds.
2025-04-29 07:39:11 +02:00
Ariel Rin
b02827cb3f Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1699
2025-04-29 02:57:50 +00:00
Ariel Rin
2bcc0570ad Updates for project Alliance Auth 2025-04-29 02:57:50 +00:00
Ariel Rin
a3ea0c65a1 Merge branch 'timerboard-skyhooks' into 'master'
Add a new `theft` timer type for skyhooks

See merge request allianceauth/allianceauth!1708
2025-04-29 02:52:38 +00:00
Ariel Rin
5e526da11c Merge branch 'add-apply_offset-to-esi_cleanup_token-task' into 'master'
[ADD] `apply_offset` to `esi_cleanup_token` task

See merge request allianceauth/allianceauth!1709
2025-04-29 02:49:56 +00:00
Ariel Rin
5c79265f90 Merge branch 'services-grid' into 'master'
[FIX] Top margin for service controls

See merge request allianceauth/allianceauth!1711
2025-04-29 02:48:11 +00:00
Ariel Rin
eb0134e716 Merge branch 'menu-separator' into 'master'
[ADD] Menu separator

See merge request allianceauth/allianceauth!1712
2025-04-29 02:48:00 +00:00
Ariel Rin
afde1f4729 Merge branch 'typehint-object-managers' into 'master'
Typehint object managers

See merge request allianceauth/allianceauth!1713
2025-04-29 02:47:12 +00:00
Ariel Rin
5c6dda0eac Typehint object managers 2025-04-29 02:47:11 +00:00
Ariel Rin
af453bc772 Merge branch 'aa_i18n-addition' into 'master'
[ADD] Temaplatetag for path relative to `static`

See merge request allianceauth/allianceauth!1714
2025-04-29 02:46:56 +00:00
Ariel Rin
e13674e886 Merge branch 'translatable-app-names' into 'master'
[CHANGE] Made app names translatable in Django admin

See merge request allianceauth/allianceauth!1715
2025-04-29 02:45:48 +00:00
Ariel Rin
e3e856b826 Merge branch 'admin-overview' into 'master'
Remove unused {% if %} tag

See merge request allianceauth/allianceauth!1718
2025-04-29 02:42:35 +00:00
T'rahk Rokym
9d1cd23a8f Remove unused {% if %} tag 2025-04-21 16:50:04 +02:00
Peter Pfeufer
148f7c116f
[CHANGE] Made app names translatable in Django admin 2025-04-10 07:08:16 +02:00
Peter Pfeufer
33e7134d6f
[ADD] Temaplatetag for path relative to static
AA-GDPR needs this
2025-04-09 20:54:51 +02:00
Peter Pfeufer
fb799551aa
[CHANGE] Move what ever is possible to our framework CSS 2025-04-06 22:22:54 +02:00
Peter Pfeufer
7b95051fe1
[ADD] Menu separator
Between `#nav-right` and `#nav-right-character-control`. Only visible when `#nav-right` has menu items.
2025-04-06 22:01:54 +02:00
Peter Pfeufer
efb6a6db4f
[FIX] Top margin for service controls 2025-04-05 23:49:58 +02:00
Peter Pfeufer
478aa1aa12
[ADD] apply_offset to esi_cleanup_token task 2025-04-03 00:59:04 +02:00
T'rahk Rokym
751e55ed6c Add a new theft timer type for skyhooks 2025-03-26 22:09:43 +01:00
Ariel Rin
9dad53f763 Merge branch 'master' into 'master'
Switch the doc example to use group instead of chain for long running tasks

See merge request allianceauth/allianceauth!1703
2025-03-26 01:48:29 +00:00
Ariel Rin
2d57064a7a Merge branch 'supervisor-config-template-updates' into 'master'
[CHANGE] Update supervisor.conf template

See merge request allianceauth/allianceauth!1700
2025-03-26 01:47:58 +00:00
Ariel Rin
833d12cf66 Merge branch 'opengraph-template' into 'master'
[ADD] Opengraph Template

See merge request allianceauth/allianceauth!1701
2025-03-26 01:47:35 +00:00
Ariel Rin
7b56caa4cb Merge branch 'perms_fix' into 'master'
Use the mumble.view_connection_history perm not superuser for Mumble service statistics

See merge request allianceauth/allianceauth!1702
2025-03-26 01:47:12 +00:00
Ariel Rin
5752644122 Merge branch 'fix-logo-height' into 'master'
[FIX] `logo_height` variable name

See merge request allianceauth/allianceauth!1704
2025-03-26 01:46:12 +00:00
Ariel Rin
cadc0cb534 Merge branch 'page-header' into 'master'
[CHANGE] Wrap page header in `header` element

See merge request allianceauth/allianceauth!1705
2025-03-26 01:46:04 +00:00
Ariel Rin
dcdab5ae1f Merge branch 'fix-spelling' into 'master'
[FIX] Spelling (It's EVE time, not Eve time)

See merge request allianceauth/allianceauth!1706
2025-03-26 01:45:58 +00:00
Ariel Rin
d64e896288 Merge branch 'fix-scrollbar-positioning' into 'master'
[FIX] Scrollbar positioning in content area

See merge request allianceauth/allianceauth!1707
2025-03-26 01:45:38 +00:00
Peter Pfeufer
500d8ede32
[FIX] Scrollbar positioning in content area 2025-03-24 17:47:40 +01:00
Peter Pfeufer
f4c5c7f6db
[FIX] Spelling (It's EVE time, not Eve time) 2025-03-24 15:14:30 +01:00
Peter Pfeufer
43e1be4032
[CHANGE] Move margin class to header element 2025-03-24 14:49:51 +01:00
Peter Pfeufer
702def2a4d
[CHANGE] Wrap page header in header element 2025-03-24 13:34:11 +01:00
Peter Pfeufer
a34baf4154
[FIX] logo_height variable name 2025-03-22 17:37:10 +01:00
r0kym
4de0774f15 Switch the doc example to use group instead of chain for long running tasks 2025-03-20 18:47:19 +01:00
Aaron Kable
83d2dfc7d9 use the view_connection_history perm not superuser 2025-03-20 19:11:08 +08:00
Peter Pfeufer
c93afd2d68
[ADD] Site name and URL 2025-03-18 21:40:47 +01:00
Peter Pfeufer
b7bacd11af
[CHANGE] Bring public base templkate in line with the non-public version 2025-03-18 18:30:00 +01:00
Peter Pfeufer
f26835fae0
[ADD] Opengraph Template for easier override 2025-03-18 18:29:22 +01:00
Peter Pfeufer
4edb7cb678
[CHANGE] Assignment format to INI style
The supervisor.conf file uses INI style, so the format should reflect this.
2025-03-16 10:15:36 +01:00
Peter Pfeufer
2ce9ba997f
[CHANGE] Update supervisor.conf template
Use `%(program_name)s` variable for logfile names, which makes it easier to avoid possible copy/paste issues and mistakes.
2025-03-16 10:11:27 +01:00
504 changed files with 6702 additions and 5132 deletions

View File

@ -1,6 +0,0 @@
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Ruff initial formatting storm
a99315ea55339f0b6010b5c9d8703e51278fcf29

1
.gitignore vendored
View File

@ -71,7 +71,6 @@ celerybeat-schedule
#other #other
.flake8 .flake8
.ruff_cache
.pylintrc .pylintrc
Makefile Makefile
alliance_auth.sqlite3 alliance_auth.sqlite3

View File

@ -25,7 +25,7 @@ before_script:
pre-commit-check: pre-commit-check:
<<: *only-default <<: *only-default
stage: pre-commit stage: pre-commit
image: python:3.12-bookworm 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,6 +51,30 @@ secret_detection:
stage: gitlab stage: gitlab
before_script: [] before_script: []
test-3.8-core:
<<: *only-default
image: python:3.8-bookworm
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-bookworm
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-bookworm image: python:3.10-bookworm
@ -87,11 +111,23 @@ test-3.12-core:
coverage_format: cobertura coverage_format: cobertura
path: coverage.xml path: coverage.xml
test-3.13-core: test-3.8-all:
<<: *only-default <<: *only-default
image: python:3.13-rc-bookworm image: python:3.8-bookworm
script: script:
- tox -e py313-core - tox -e py38-all
artifacts:
when: always
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
test-3.9-all:
<<: *only-default
image: python:3.9-bookworm
script:
- tox -e py39-all
artifacts: artifacts:
when: always when: always
reports: reports:
@ -136,21 +172,9 @@ 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
build-test: build-test:
stage: test stage: test
image: python:3.12-bookworm image: python:3.11-bookworm
before_script: before_script:
- python -m pip install --upgrade pip - python -m pip install --upgrade pip
@ -169,13 +193,13 @@ build-test:
test-docs: test-docs:
<<: *only-default <<: *only-default
image: python:3.12-bookworm image: python:3.11-bookworm
script: script:
- tox -e docs - tox -e docs
deploy_production: deploy_production:
stage: deploy stage: deploy
image: python:3.12-bookworm image: python:3.11-bookworm
before_script: before_script:
- python -m pip install --upgrade pip - python -m pip install --upgrade pip
@ -191,10 +215,10 @@ deploy_production:
build-image: build-image:
before_script: [] before_script: []
image: docker:27 image: docker:24.0
stage: docker stage: docker
services: services:
- docker:27-dind - docker:24.0-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
@ -215,10 +239,10 @@ build-image:
build-image-dev: build-image-dev:
before_script: [] before_script: []
image: docker:27 image: docker:24.0
stage: docker stage: docker
services: services:
- docker:27-dind - docker:24.0-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
@ -236,10 +260,10 @@ build-image-dev:
build-image-mr: build-image-mr:
before_script: [] before_script: []
image: docker:27 image: docker:24.0
stage: docker stage: docker
services: services:
- docker:27-dind - docker:24.0-dind
script: | script: |
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -) CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA

View File

@ -19,22 +19,21 @@ exclude: |
\.po| \.po|
\.mo| \.mo|
swagger\.json| swagger\.json|
static/(.*)/libs/| static/(.*)/libs/
telnetlib\.py
) )
repos: repos:
# Code Upgrades # Code Upgrades
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.25.0
hooks:
- id: django-upgrade
args: [--target-version=5.2]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v3.20.0 rev: v3.20.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py310-plus] args: [--py38-plus]
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.25.0
hooks:
- id: django-upgrade
args: [--target-version=4.2]
# Formatting # Formatting
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
@ -77,21 +76,20 @@ repos:
language: node language: node
args: args:
- --disable=MD013 - --disable=MD013
# Infrastructure # Infrastructure
- repo: https://github.com/tox-dev/pyproject-fmt - repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.6.0 rev: v2.6.0
hooks: hooks:
- id: pyproject-fmt - id: pyproject-fmt
name: pyproject.toml formatter
description: "Format the pyproject.toml file."
args: args:
- --indent=4 - --indent=4
additional_dependencies: additional_dependencies:
- tox==4.26.0 # https://github.com/tox-dev/tox/releases/latest - tox==4.24.1 # https://github.com/tox-dev/tox/releases/latest
- repo: https://github.com/tox-dev/tox-ini-fmt
rev: 1.5.0
hooks:
- id: tox-ini-fmt
- repo: https://github.com/abravalheri/validate-pyproject - repo: https://github.com/abravalheri/validate-pyproject
rev: v0.24.1 rev: v0.24.1
hooks: hooks:
- id: validate-pyproject - id: validate-pyproject
name: Validate pyproject.toml
description: "Validate the pyproject.toml file."

View File

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

View File

@ -1,15 +1,15 @@
# Alliance Auth # Alliance Auth
[![license](https://img.shields.io/badge/license-GPLv2-green)](https://pypi.org/project/allianceauth/) [![License](https://img.shields.io/badge/license-GPLv2-green)](https://pypi.org/project/allianceauth/)
[![python](https://img.shields.io/pypi/pyversions/allianceauth)](https://pypi.org/project/allianceauth/) [![Python Versions](https://img.shields.io/pypi/pyversions/allianceauth)](https://pypi.org/project/allianceauth/)
[![django](https://img.shields.io/pypi/djversions/allianceauth?label=django)](https://pypi.org/project/allianceauth/) [![Django Versions](https://img.shields.io/pypi/djversions/allianceauth?label=django)](https://pypi.org/project/allianceauth/)
[![version](https://img.shields.io/pypi/v/allianceauth?label=release)](https://pypi.org/project/allianceauth/) [![Stable AA Version](https://img.shields.io/pypi/v/allianceauth?label=release)](https://pypi.org/project/allianceauth/)
[![pipeline status](https://gitlab.com/allianceauth/allianceauth/badges/master/pipeline.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master) [![Pipeline Status](https://gitlab.com/allianceauth/allianceauth/badges/master/pipeline.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master)
[![Documentation Status](https://readthedocs.org/projects/allianceauth/badge/?version=latest)](https://allianceauth.readthedocs.io/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/allianceauth/badge/?version=latest)](https://allianceauth.readthedocs.io/?badge=latest)
[![coverage report](https://gitlab.com/allianceauth/allianceauth/badges/master/coverage.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master) [![Test Coverage Report](https://gitlab.com/allianceauth/allianceauth/badges/master/coverage.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master)
[![Chat on Discord](https://img.shields.io/discord/399006117012832262.svg)](https://discord.gg/fjnHAmk) [![Chat on Discord](https://img.shields.io/discord/399006117012832262.svg)](https://discord.gg/fjnHAmk)
An auth system for EVE Online to help in-game organizations manage online service access. A flexible authentication platform for EVE Online to help in-game organizations manage access to applications and services. AA provides both, a stable core, and a robust framework for community development and custom applications.
## Content ## Content
@ -22,17 +22,17 @@ An auth system for EVE Online to help in-game organizations manage online servic
## Overview ## Overview
Alliance Auth (AA) is a web site that helps Eve Online organizations efficiently manage access to applications and services. Alliance Auth (AA) is a platform that helps Eve Online organizations efficiently manage access to applications and services.
Main features: Main features:
- Automatically grants or revokes user access to external services (e.g. Discord, Mumble) and web apps (e.g. SRP requests) based on the user's current membership to [in-game organizations](https://allianceauth.readthedocs.io/en/latest/features/core/states/) and [groups](https://allianceauth.readthedocs.io/en/latest/features/core/groups/) - Automatically grants or revokes user access to external services (e.g.: Discord, Mumble) based on the user's current membership to [a variety of EVE Online affiliation](https://allianceauth.readthedocs.io/en/latest/features/core/states/) and [groups](https://allianceauth.readthedocs.io/en/latest/features/core/groups/)
- Provides a central web site where users can directly access web apps (e.g. SRP requests, Fleet Schedule) and manage their access to external services and groups. - Provides a central web site where users can directly access web apps (e.g. SRP requests, Fleet Schedule) and manage their access to external services and groups.
- Includes a set of connectors (called ["services"](https://allianceauth.readthedocs.io/en/latest/features/services/)) for integrating access management with many popular external applications / services like Discord, Mumble, Teamspeak 3, SMF and others - Includes a set of connectors (called ["Services"](https://allianceauth.readthedocs.io/en/latest/features/services/)) for integrating access management with many popular external applications / services like Discord, Mumble, Teamspeak 3, SMF and others
- Includes a set of web [apps](https://allianceauth.readthedocs.io/en/latest/features/apps/) which add many useful functions, e.g.: fleet schedule, timer board, SRP request management, fleet activity tracker - Includes a set of web [Apps](https://allianceauth.readthedocs.io/en/latest/features/apps/) which add many useful functions, e.g.: fleet schedule, timer board, SRP request management, fleet activity tracker
- Can be easily extended with additional services and apps. Many are provided by the community and can be found here: [Community Creations](https://gitlab.com/allianceauth/community-creations) - Can be easily extended with additional services and apps. Many are provided by the community and can be found here: [Community Creations](https://gitlab.com/allianceauth/community-creations)
@ -42,9 +42,15 @@ For further details about AA - including an installation guide and a full list o
## Screenshot ## Screenshot
Here is an example of the Alliance Auth web site with some plug-ins apps and services enabled: Here is an example of the Alliance Auth web site with a mixture of Services, Apps and Community Creations enabled:
![screenshot](https://i.imgur.com/2tnX9kD.png) ### Flatly Theme
![Flatly Theme](docs/_static/images/promotion/SampleInstallation-Flatly.png)
### Darkly Theme
![Darkly Theme](docs/_static/images/promotion/SampleInstallation-Darkly.png)
## Support ## Support

View File

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

View File

@ -1,8 +1,7 @@
from solo.admin import SingletonModelAdmin
from django.contrib import admin from django.contrib import admin
from .models import AnalyticsIdentifier, AnalyticsTokens from .models import AnalyticsIdentifier, AnalyticsTokens
from solo.admin import SingletonModelAdmin
@admin.register(AnalyticsIdentifier) @admin.register(AnalyticsIdentifier)

View File

@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class AnalyticsConfig(AppConfig): class AnalyticsConfig(AppConfig):
name = 'allianceauth.analytics' name = 'allianceauth.analytics'
label = 'analytics' label = 'analytics'
verbose_name = _('Analytics')

View File

@ -1,8 +1,7 @@
# Generated by Django 3.1.4 on 2020-12-30 13:11 # Generated by Django 3.1.4 on 2020-12-30 13:11
import uuid
from django.db import migrations, models from django.db import migrations, models
import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -21,7 +21,7 @@ def remove_aa_team_token(apps, schema_editor):
# Have to define some code to remove this identifier # Have to define some code to remove this identifier
# In case of migration rollback? # In case of migration rollback?
Tokens = apps.get_model('analytics', 'AnalyticsTokens') Tokens = apps.get_model('analytics', 'AnalyticsTokens')
Tokens.objects.filter(token="UA-186249766-2").delete() token = Tokens.objects.filter(token="UA-186249766-2").delete()
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -1,5 +1,6 @@
# Generated by Django 3.1.4 on 2020-12-30 08:53 # Generated by Django 3.1.4 on 2020-12-30 08:53
from uuid import uuid4
from django.db import migrations from django.db import migrations

View File

@ -1,7 +1,7 @@
# Generated by Django 3.1.4 on 2020-12-30 08:53 # Generated by Django 3.1.4 on 2020-12-30 08:53
from django.core.exceptions import ObjectDoesNotExist
from django.db import migrations from django.db import migrations
from django.core.exceptions import ObjectDoesNotExist
def add_aa_team_token(apps, schema_editor): def add_aa_team_token(apps, schema_editor):
@ -51,7 +51,7 @@ def remove_aa_team_token(apps, schema_editor):
# Have to define some code to remove this identifier # Have to define some code to remove this identifier
# In case of migration rollback? # In case of migration rollback?
Tokens = apps.get_model('analytics', 'AnalyticsTokens') Tokens = apps.get_model('analytics', 'AnalyticsTokens')
Tokens.objects.filter(token="G-6LYSMYK8DE").delete() token = Tokens.objects.filter(token="G-6LYSMYK8DE").delete()
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -1,10 +1,8 @@
from typing import Literal from typing import Literal
from uuid import uuid4
from solo.models import SingletonModel
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from solo.models import SingletonModel
from uuid import uuid4
class AnalyticsIdentifier(SingletonModel): class AnalyticsIdentifier(SingletonModel):
@ -17,6 +15,7 @@ class AnalyticsIdentifier(SingletonModel):
class Meta: class Meta:
verbose_name = "Analytics Identifier" verbose_name = "Analytics Identifier"
class AnalyticsTokens(models.Model): class AnalyticsTokens(models.Model):
class Analytics_Type(models.TextChoices): class Analytics_Type(models.TextChoices):
@ -28,6 +27,3 @@ class AnalyticsTokens(models.Model):
token = models.CharField(max_length=254, blank=False) token = models.CharField(max_length=254, blank=False)
secret = models.CharField(max_length=254, blank=True) secret = models.CharField(max_length=254, blank=True)
send_stats = models.BooleanField(default=False) send_stats = models.BooleanField(default=False)
def __str__(self) -> str:
return self.name

View File

@ -1,16 +1,17 @@
import logging
import requests import requests
from celery import shared_task import logging
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.apps import apps
from celery import shared_task
from .models import AnalyticsTokens, AnalyticsIdentifier
from .utils import (
existence_baremetal_or_docker,
install_stat_addons,
install_stat_tokens,
install_stat_users)
from allianceauth import __version__ from allianceauth import __version__
from .models import AnalyticsIdentifier, AnalyticsTokens
from .utils import existence_baremetal_or_docker, install_stat_addons, install_stat_tokens, install_stat_users
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
BASE_URL = "https://www.google-analytics.com" BASE_URL = "https://www.google-analytics.com"

View File

@ -1,8 +1,9 @@
from uuid import uuid4 from allianceauth.analytics.models import AnalyticsIdentifier
from django.test.testcases import TestCase from django.test.testcases import TestCase
from allianceauth.analytics.models import AnalyticsIdentifier from uuid import uuid4
# Identifiers # Identifiers
uuid_1 = "ab33e241fbf042b6aa77c7655a768af7" uuid_1 = "ab33e241fbf042b6aa77c7655a768af7"

View File

@ -2,9 +2,12 @@ import requests_mock
from django.test.utils import override_settings from django.test.utils import override_settings
from allianceauth.analytics.tasks import analytics_event, send_ga_tracking_celery_event from allianceauth.analytics.tasks import (
analytics_event,
send_ga_tracking_celery_event)
from allianceauth.utils.testing import NoSocketsTestCase from allianceauth.utils.testing import NoSocketsTestCase
GOOGLE_ANALYTICS_DEBUG_URL = 'https://www.google-analytics.com/debug/mp/collect' GOOGLE_ANALYTICS_DEBUG_URL = 'https://www.google-analytics.com/debug/mp/collect'

View File

@ -1,8 +1,9 @@
from django.apps import apps from django.apps import apps
from django.test.testcases import TestCase
from allianceauth.analytics.utils import install_stat_addons, install_stat_users
from allianceauth.authentication.models import User from allianceauth.authentication.models import User
from esi.models import Token
from allianceauth.analytics.utils import install_stat_users, install_stat_tokens, install_stat_addons
from django.test.testcases import TestCase
def create_testdata(): def create_testdata():

View File

@ -1,10 +1,7 @@
import os import os
from django.apps import apps from django.apps import apps
from esi.models import Token
from allianceauth.authentication.models import User from allianceauth.authentication.models import User
from esi.models import Token
def install_stat_users() -> int: def install_stat_users() -> int:

View File

@ -1,21 +1,43 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import Group, Permission as BasePermission, User as BaseUser from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission as BasePermission
from django.contrib.auth.models import User as BaseUser
from django.db.models import Count, Q from django.db.models import Count, Q
from django.db.models.functions import Lower from django.db.models.functions import Lower
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete, pre_save from django.db.models.signals import (
m2m_changed,
post_delete,
post_save,
pre_delete,
pre_save
)
from django.dispatch import receiver from django.dispatch import receiver
from django.urls import reverse from django.urls import reverse
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.text import slugify from django.utils.text import slugify
from allianceauth.authentication.models import CharacterOwnership, OwnershipRecord, State, UserProfile, get_guest_state from allianceauth.authentication.models import (
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo, EveFactionInfo CharacterOwnership,
OwnershipRecord,
State,
UserProfile,
get_guest_state
)
from allianceauth.eveonline.models import (
EveAllianceInfo,
EveCharacter,
EveCorporationInfo,
EveFactionInfo
)
from allianceauth.eveonline.tasks import update_character from allianceauth.eveonline.tasks import update_character
from allianceauth.hooks import get_hooks from allianceauth.hooks import get_hooks
from allianceauth.services.hooks import ServicesHook from allianceauth.services.hooks import ServicesHook
from .app_settings import AUTHENTICATION_ADMIN_USERS_MAX_CHARS, AUTHENTICATION_ADMIN_USERS_MAX_GROUPS from .app_settings import (
AUTHENTICATION_ADMIN_USERS_MAX_CHARS,
AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
)
from .forms import UserChangeForm, UserProfileForm from .forms import UserChangeForm, UserProfileForm
@ -110,7 +132,10 @@ def user_username(obj):
To be used for all user based admin lists To be used for all user based admin lists
""" """
link = reverse( link = reverse(
f'admin:{obj._meta.app_label}_{type(obj).__name__.lower()}_change', 'admin:{}_{}_change'.format(
obj._meta.app_label,
type(obj).__name__.lower()
),
args=(obj.pk,) args=(obj.pk,)
) )
user_obj = obj.user if hasattr(obj, 'user') else obj user_obj = obj.user if hasattr(obj, 'user') else obj
@ -523,7 +548,7 @@ class BaseOwnershipAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
if obj and obj.pk: if obj and obj.pk:
return 'owner_hash', 'character' return 'owner_hash', 'character'
return () return tuple()
@admin.register(OwnershipRecord) @admin.register(OwnershipRecord)

View File

@ -25,7 +25,7 @@ def _clean_setting(
if not required_type: if not required_type:
required_type = type(default_value) required_type = type(default_value)
if min_value is None and required_type is int: if min_value is None and required_type == int:
min_value = 0 min_value = 0
if (hasattr(settings, name) if (hasattr(settings, name)

View File

@ -1,10 +1,12 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.core.checks import Tags, register from django.core.checks import register, Tags
from django.utils.translation import gettext_lazy as _
class AuthenticationConfig(AppConfig): class AuthenticationConfig(AppConfig):
name = "allianceauth.authentication" name = "allianceauth.authentication"
label = "authentication" label = "authentication"
verbose_name = _("Authentication")
def ready(self): def ready(self):
from allianceauth.authentication import checks, signals # noqa: F401 from allianceauth.authentication import checks, signals # noqa: F401

View File

@ -1,7 +1,6 @@
from allianceauth import hooks
from allianceauth.hooks import DashboardItemHook from allianceauth.hooks import DashboardItemHook
from allianceauth import hooks
from .views import dashboard_admin, dashboard_characters, dashboard_esi_check, dashboard_groups from .views import dashboard_characters, dashboard_esi_check, dashboard_groups, dashboard_admin
class UserCharactersHook(DashboardItemHook): class UserCharactersHook(DashboardItemHook):

View File

@ -1,9 +1,10 @@
import logging import logging
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import Permission, User from django.contrib.auth.models import User, Permission
from .models import UserProfile, CharacterOwnership, OwnershipRecord
from .models import CharacterOwnership, OwnershipRecord, UserProfile
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,5 +1,5 @@
from django.conf import settings
from django.core.checks import Error from django.core.checks import Error
from django.conf import settings
def check_login_scopes_setting(*args, **kwargs): def check_login_scopes_setting(*args, **kwargs):

View File

@ -2,6 +2,7 @@
import itertools import itertools
import logging import logging
from typing import Optional
from amqp.exceptions import ChannelError from amqp.exceptions import ChannelError
from celery import current_app from celery import current_app
@ -11,7 +12,7 @@ from django.conf import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def active_tasks_count() -> int | None: def active_tasks_count() -> Optional[int]:
"""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.
""" """
@ -19,7 +20,7 @@ def active_tasks_count() -> int | None:
return _tasks_count(inspect.active()) return _tasks_count(inspect.active())
def _tasks_count(data: dict) -> int | None: def _tasks_count(data: dict) -> Optional[int]:
"""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())
@ -28,7 +29,7 @@ def _tasks_count(data: dict) -> int | None:
return len(list(tasks)) return len(list(tasks))
def queued_tasks_count() -> int | None: def queued_tasks_count() -> Optional[int]:
"""Return count of queued tasks. Return None if there was an error.""" """Return count of queued tasks. Return None if there was an error."""
try: try:
with current_app.connection_or_acquire() as conn: with current_app.connection_or_acquire() as conn:

View File

@ -1,11 +1,14 @@
from collections.abc import Callable, Iterable from django.urls import include
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
from functools import wraps from functools import wraps
from typing import Callable, Iterable, Optional
from django.urls import include
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.decorators import login_required, user_passes_test
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import include
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -14,7 +17,7 @@ def user_has_main_character(user):
def decorate_url_patterns( def decorate_url_patterns(
urls, decorator: Callable, excluded_views: Iterable | None = None urls, decorator: Callable, excluded_views: Optional[Iterable] = None
): ):
"""Decorate views given in url patterns except when they are explicitly excluded. """Decorate views given in url patterns except when they are explicitly excluded.

View File

@ -60,7 +60,7 @@ class UserChangeForm(BaseUserChangeForm):
{ {
"groups": _( "groups": _(
"You are not allowed to add or remove these " "You are not allowed to add or remove these "
"restricted groups: {}".format(restricted_names) "restricted groups: %s" % restricted_names
) )
} }
) )

View File

@ -1,6 +1,5 @@
from django.urls import include, path, re_path
from allianceauth.authentication import views from allianceauth.authentication import views
from django.urls import include, re_path, path
urlpatterns = [ urlpatterns = [
path('activate/complete/', views.activation_complete, name='registration_activation_complete'), path('activate/complete/', views.activation_complete, name='registration_activation_complete'),

View File

@ -1,5 +1,4 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from allianceauth.authentication.models import UserProfile from allianceauth.authentication.models import UserProfile
@ -12,7 +11,8 @@ class Command(BaseCommand):
if profiles.exists(): if profiles.exists():
for profile in profiles: for profile in profiles:
self.stdout.write(self.style.ERROR( self.stdout.write(self.style.ERROR(
f'{profile.main_character} does not have an ownership. Resetting user {profile.user} main character.')) '{} does not have an ownership. Resetting user {} main character.'.format(profile.main_character,
profile.user)))
profile.main_character = None profile.main_character = None
profile.save() profile.save()
self.stdout.write(self.style.WARNING(f'Reset {profiles.count()} main characters.')) self.stdout.write(self.style.WARNING(f'Reset {profiles.count()} main characters.'))

View File

@ -1,7 +1,7 @@
import logging import logging
from django.db import transaction from django.db import transaction
from django.db.models import Manager, Q, QuerySet from django.db.models import Manager, QuerySet, Q
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter

View File

@ -1,8 +1,8 @@
import logging
from django.conf import settings from django.conf import settings
from django.utils.deprecation import MiddlewareMixin from django.utils.deprecation import MiddlewareMixin
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,8 +1,8 @@
# Generated by Django 1.10.1 on 2016-09-05 21:38 # Generated by Django 1.10.1 on 2016-09-05 21:38
import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -2,7 +2,6 @@
from django.db import migrations from django.db import migrations
def create_permissions(apps, schema_editor): def create_permissions(apps, schema_editor):
User = apps.get_model('auth', 'User') User = apps.get_model('auth', 'User')
ContentType = apps.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')

View File

@ -2,7 +2,6 @@
from django.db import migrations from django.db import migrations
def delete_permissions(apps, schema_editor): def delete_permissions(apps, schema_editor):
User = apps.get_model('auth', 'User') User = apps.get_model('auth', 'User')
ContentType = apps.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')

View File

@ -2,7 +2,6 @@
from django.db import migrations from django.db import migrations
def count_completed_fields(model): def count_completed_fields(model):
return len([True for key, value in model.__dict__.items() if bool(value)]) return len([True for key, value in model.__dict__.items() if bool(value)])

View File

@ -1,8 +1,8 @@
# Generated by Django 1.10.1 on 2017-01-07 07:11 # Generated by Django 1.10.1 on 2017-01-07 07:11
import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -1,7 +1,6 @@
# Generated by Django 1.10.5 on 2017-01-12 00:59 # Generated by Django 1.10.5 on 2017-01-12 00:59
from django.db import migrations from django.db import migrations, models
def remove_permissions(apps, schema_editor): def remove_permissions(apps, schema_editor):
ContentType = apps.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')

View File

@ -1,9 +1,9 @@
# Generated by Django 1.10.2 on 2016-12-11 23:14 # Generated by Django 1.10.2 on 2016-12-11 23:14
import logging
from django.db import migrations from django.db import migrations
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,12 +1,11 @@
# Generated by Django 1.10.5 on 2017-03-22 23:09 # Generated by Django 1.10.5 on 2017-03-22 23:09
import allianceauth.authentication.models
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.contrib.auth.hashers import make_password from django.contrib.auth.hashers import make_password
from django.db import migrations, models from django.db import migrations, models
import allianceauth.authentication.models
def create_guest_state(apps, schema_editor): def create_guest_state(apps, schema_editor):
State = apps.get_model('authentication', 'State') State = apps.get_model('authentication', 'State')

View File

@ -1,8 +1,8 @@
# Generated by Django 2.0.4 on 2018-04-14 18:28 # Generated by Django 2.0.4 on 2018-04-14 18:28
import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
def create_initial_records(apps, schema_editor): def create_initial_records(apps, schema_editor):

View File

@ -1,11 +1,12 @@
import logging import logging
from typing import ClassVar
from django.contrib.auth.models import Permission, User from django.contrib.auth.models import User, Permission
from django.db import models, transaction from django.db import models, transaction
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo, EveFactionInfo
from allianceauth.notifications import notify from allianceauth.notifications import notify
from django.conf import settings
from .managers import CharacterOwnershipManager, StateManager from .managers import CharacterOwnershipManager, StateManager
@ -27,12 +28,12 @@ class State(models.Model):
help_text="Factions to whose members this state is available.") help_text="Factions to whose members this state is available.")
public = models.BooleanField(default=False, help_text="Make this state available to any character.") public = models.BooleanField(default=False, help_text="Make this state available to any character.")
objects = StateManager() objects: ClassVar[StateManager] = StateManager()
class Meta: class Meta:
ordering = ['-priority'] ordering = ['-priority']
def __str__(self) -> str: def __str__(self):
return self.name return self.name
def available_to_character(self, character): def available_to_character(self, character):
@ -60,7 +61,8 @@ def get_guest_state_pk():
class UserProfile(models.Model): class UserProfile(models.Model):
class Meta:
default_permissions = ('change',)
class Language(models.TextChoices): class Language(models.TextChoices):
""" """
@ -107,15 +109,10 @@ class UserProfile(models.Model):
_("Theme"), _("Theme"),
max_length=200, max_length=200,
blank=True, blank=True,
null=True,
help_text="Bootstrap 5 Themes from https://bootswatch.com/ or Community Apps" help_text="Bootstrap 5 Themes from https://bootswatch.com/ or Community Apps"
) )
class Meta:
default_permissions = ('change',)
def __str__(self) -> str:
return str(self.user)
def assign_state(self, state=None, commit=True): def assign_state(self, state=None, commit=True):
if not state: if not state:
state = State.objects.get_for_user(self.user) state = State.objects.get_for_user(self.user)
@ -126,7 +123,7 @@ class UserProfile(models.Model):
self.save(update_fields=['state']) self.save(update_fields=['state'])
notify( notify(
self.user, self.user,
_(f'State changed to: {state}'), _('State changed to: %s' % state),
_('Your user\'s state is now: %(state)s') _('Your user\'s state is now: %(state)s')
% ({'state': state}), % ({'state': state}),
'info' 'info'
@ -141,19 +138,22 @@ class UserProfile(models.Model):
sender=self.__class__, user=self.user, state=self.state sender=self.__class__, user=self.user, state=self.state
) )
def __str__(self) -> str:
return str(self.user)
class CharacterOwnership(models.Model): class CharacterOwnership(models.Model):
class Meta:
default_permissions = ('change', 'delete')
ordering = ['user', 'character__character_name']
character = models.OneToOneField(EveCharacter, on_delete=models.CASCADE, related_name='character_ownership') character = models.OneToOneField(EveCharacter, on_delete=models.CASCADE, related_name='character_ownership')
owner_hash = models.CharField(max_length=28, unique=True) owner_hash = models.CharField(max_length=28, unique=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='character_ownerships') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='character_ownerships')
objects = CharacterOwnershipManager() objects: ClassVar[CharacterOwnershipManager] = CharacterOwnershipManager()
class Meta:
default_permissions = ('change', 'delete') def __str__(self):
ordering = ['user', 'character__character_name']
def __str__(self) -> str:
return f"{self.user}: {self.character}" return f"{self.user}: {self.character}"
@ -166,5 +166,5 @@ class OwnershipRecord(models.Model):
class Meta: class Meta:
ordering = ['-created'] ordering = ['-created']
def __str__(self) -> str: def __str__(self):
return f"{self.user}: {self.character} on {self.created}" return f"{self.user}: {self.character} on {self.created}"

View File

@ -1,16 +1,19 @@
import logging import logging
from .models import (
CharacterOwnership,
UserProfile,
get_guest_state,
State,
OwnershipRecord)
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import Q from django.db.models import Q
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete, pre_save from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
from django.dispatch import Signal, receiver from django.dispatch import receiver, Signal
from esi.models import Token from esi.models import Token
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter
from .models import CharacterOwnership, OwnershipRecord, State, UserProfile, get_guest_state
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
state_changed = Signal() state_changed = Signal()
@ -105,7 +108,8 @@ def record_character_ownership(sender, instance, created, *args, **kwargs):
def validate_main_character(sender, instance, *args, **kwargs): def validate_main_character(sender, instance, *args, **kwargs):
try: try:
if instance.user.profile.main_character == instance.character: if instance.user.profile.main_character == instance.character:
logger.info(f"Ownership of a main character {instance.character} has been revoked. Resetting {instance.user} main character.") logger.info("Ownership of a main character {} has been revoked. Resetting {} main character.".format(
instance.character, instance.user))
# clear main character as user no longer owns them # clear main character as user no longer owns them
instance.user.profile.main_character = None instance.user.profile.main_character = None
instance.user.profile.save() instance.user.profile.save()

View File

@ -1,7 +1,7 @@
"""Counters for Task Statistics.""" """Counters for Task Statistics."""
import datetime as dt import datetime as dt
from typing import NamedTuple from typing import NamedTuple, Optional
from .event_series import EventSeries from .event_series import EventSeries
@ -16,7 +16,7 @@ class _TaskCounts(NamedTuple):
retried: int retried: int
failed: int failed: int
total: int total: int
earliest_task: dt.datetime | None earliest_task: Optional[dt.datetime]
hours: int hours: int

View File

@ -2,6 +2,7 @@
import datetime as dt import datetime as dt
import logging import logging
from typing import List, Optional
from pytz import utc from pytz import utc
from redis import Redis from redis import Redis
@ -16,7 +17,7 @@ class EventSeries:
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES" _ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
def __init__(self, key_id: str, redis: Redis | None = None) -> None: def __init__(self, key_id: str, redis: Optional[Redis] = 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()
@ -45,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]
@ -74,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) -> dt.datetime | None: def first_event(self, earliest: dt.datetime = None) -> Optional[dt.datetime]:
"""Date/Time of first event. Returns `None` if series has no events. """Date/Time of first event. Returns `None` if series has no events.
Args: Args:

View File

@ -1,11 +1,7 @@
"""Signals for Task Statistics.""" """Signals for Task Statistics."""
from celery.signals import ( from celery.signals import (
task_failure, task_failure, task_internal_error, task_retry, task_success, worker_ready,
task_internal_error,
task_retry,
task_success,
worker_ready,
) )
from django.conf import settings from django.conf import settings

View File

@ -4,10 +4,7 @@ from django.test import TestCase
from django.utils.timezone import now from django.utils.timezone import now
from allianceauth.authentication.task_statistics.counters import ( from allianceauth.authentication.task_statistics.counters import (
dashboard_results, dashboard_results, failed_tasks, retried_tasks, succeeded_tasks,
failed_tasks,
retried_tasks,
succeeded_tasks,
) )

View File

@ -4,8 +4,7 @@ from unittest.mock import patch
from redis import RedisError from redis import RedisError
from allianceauth.authentication.task_statistics.helpers import ( from allianceauth.authentication.task_statistics.helpers import (
_RedisStub, _RedisStub, get_redis_client_or_stub,
get_redis_client_or_stub,
) )
MODULE_PATH = "allianceauth.authentication.task_statistics.helpers" MODULE_PATH = "allianceauth.authentication.task_statistics.helpers"

View File

@ -10,8 +10,8 @@ from allianceauth.authentication.task_statistics.counters import (
succeeded_tasks, succeeded_tasks,
) )
from allianceauth.authentication.task_statistics.signals import ( from allianceauth.authentication.task_statistics.signals import (
is_enabled,
reset_counters, reset_counters,
is_enabled,
) )
from allianceauth.eveonline.tasks import update_character from allianceauth.eveonline.tasks import update_character

View File

@ -1,9 +1,8 @@
import logging import logging
from celery import shared_task from esi.errors import TokenExpiredError, TokenInvalidError, IncompleteResponseError
from esi.errors import IncompleteResponseError, TokenExpiredError, TokenInvalidError
from esi.models import Token from esi.models import Token
from celery import shared_task
from allianceauth.authentication.models import CharacterOwnership from allianceauth.authentication.models import CharacterOwnership
@ -23,7 +22,8 @@ def check_character_ownership(owner_hash):
continue continue
except (KeyError, IncompleteResponseError): except (KeyError, IncompleteResponseError):
# We can't validate the hash hasn't changed but also can't assume it has. Abort for now. # We can't validate the hash hasn't changed but also can't assume it has. Abort for now.
logger.warning(f"Failed to validate owner hash of {tokens[0].character_name} due to problems contacting SSO servers.") logger.warning("Failed to validate owner hash of {} due to problems contacting SSO servers.".format(
tokens[0].character_name))
break break
if not t.character_owner_hash == old_hash: if not t.character_owner_hash == old_hash:
@ -33,7 +33,7 @@ def check_character_ownership(owner_hash):
break break
if not Token.objects.filter(character_owner_hash=owner_hash).exists(): if not Token.objects.filter(character_owner_hash=owner_hash).exists():
logger.info(f'No tokens found with owner hash {owner_hash}. Revoking ownership.') logger.info('No tokens found with owner hash %s. Revoking ownership.' % owner_hash)
CharacterOwnership.objects.filter(owner_hash=owner_hash).delete() CharacterOwnership.objects.filter(owner_hash=owner_hash).delete()

View File

@ -0,0 +1,10 @@
{% extends 'allianceauth/base.html' %}
{% block page_title %}Dashboard{% endblock page_title %}
{% block content %}
<div>
<h1>Dashboard Dummy</h1>
</div>
{% endblock %}

View File

@ -31,7 +31,7 @@
<tr> <tr>
<td style="white-space:initial;"> <td style="white-space:initial;">
{% for s in t.scopes.all %} {% for s in t.scopes.all %}
<span class="badge bg-secondary">{{ s.name }}</span> <span class="badge text-bg-secondary">{{ s.name }}</span>
{% endfor %} {% endfor %}
</td> </td>

View File

@ -1,24 +1,24 @@
{% load theme_tags %} {% load theme_tags %}
{% load static %} {% load static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" {% theme_html_tags %}>
<head> <head>
<!-- Required meta tags -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content=""> <!-- End Required meta tags -->
<meta name="author" content="">
<!-- TODO Bundle all the site specific stuff up into its own template for easy override -->
<meta property="og:title" content="{{ SITE_NAME }}">
<meta property="og:image" content="{{ SITE_URL }}{% static 'allianceauth/icons/apple-touch-icon.png' %}">
<meta property="og:description" content="Alliance Auth - An auth system for EVE Online to help in-game organizations manage online service access.">
<!-- Meta tags -->
{% include 'allianceauth/opengraph.html' %}
{% include 'allianceauth/icons.html' %} {% include 'allianceauth/icons.html' %}
<!-- Meta tags -->
<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>
{% theme_css %} {% theme_css %}
{% include 'bundles/fontawesome.html' %} {% include 'bundles/fontawesome.html' %}
{% include 'bundles/auth-framework-css.html' %}
{% block extra_include %} {% block extra_include %}
{% endblock %} {% endblock %}

View File

@ -1,17 +1,15 @@
{% load i18n %} {% load i18n %}
<div class="dropdown"> <form class="dropdown-item" action="{% url 'set_language' %}" method="post">
<form action="{% url 'set_language' %}" method="post"> {% csrf_token %}
{% csrf_token %}
<select class="form-select" onchange="this.form.submit()" class="form-control" id="lang-select" name="language"> <select class="form-select" onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
{% get_available_languages as LANGUAGES %} {% get_available_languages as LANGUAGES %}
{% for lang_code, lang_name in LANGUAGES %} {% for lang_code, lang_name in LANGUAGES %}
<option lang="{{ lang_code }}" value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}> <option lang="{{ lang_code }}" value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
{{ lang_code|language_name_local|capfirst }} ({{ lang_code }}) {{ lang_code|language_name_local|capfirst }} ({{ lang_code }})
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
</form> </form>
</div>

View File

@ -1,6 +1,11 @@
from unittest import mock from django.db.models.signals import (
m2m_changed,
post_save,
pre_delete,
pre_save
)
from django.urls import reverse from django.urls import reverse
from unittest import mock
MODULE_PATH = 'allianceauth.authentication' MODULE_PATH = 'allianceauth.authentication'
@ -12,7 +17,9 @@ def patch(target, *args, **kwargs):
def get_admin_change_view_url(obj: object) -> str: def get_admin_change_view_url(obj: object) -> str:
"""returns URL to admin change view for given object""" """returns URL to admin change view for given object"""
return reverse( return reverse(
f'admin:{obj._meta.app_label}_{type(obj).__name__.lower()}_change', 'admin:{}_{}_change'.format(
obj._meta.app_label, type(obj).__name__.lower()
),
args=(obj.pk,) args=(obj.pk,)
) )

View File

@ -5,8 +5,7 @@ from amqp.exceptions import ChannelError
from django.test import TestCase from django.test import TestCase
from allianceauth.authentication.core.celery_workers import ( from allianceauth.authentication.core.celery_workers import (
active_tasks_count, active_tasks_count, queued_tasks_count,
queued_tasks_count,
) )
MODULE_PATH = "allianceauth.authentication.core.celery_workers" MODULE_PATH = "allianceauth.authentication.core.celery_workers"

View File

@ -1,37 +1,42 @@
from unittest.mock import MagicMock, patch
from urllib.parse import quote
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from urllib.parse import quote
from unittest.mock import patch, MagicMock
from django_webtest import WebTest from django_webtest import WebTest
from django.contrib.admin.sites import AdminSite from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.test import Client, RequestFactory, TestCase from django.test import TestCase, RequestFactory, Client
from allianceauth.authentication.models import CharacterOwnership, OwnershipRecord, State from allianceauth.authentication.models import (
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo, EveFactionInfo CharacterOwnership, State, OwnershipRecord
)
from allianceauth.eveonline.models import (
EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
)
from allianceauth.services.hooks import ServicesHook from allianceauth.services.hooks import ServicesHook
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from ..admin import ( from ..admin import (
BaseUserAdmin, BaseUserAdmin,
CharacterOwnershipAdmin, CharacterOwnershipAdmin,
MainAllianceFilter, StateAdmin,
MainCorporationsFilter, MainCorporationsFilter,
MainAllianceFilter,
MainFactionFilter, MainFactionFilter,
OwnershipRecordAdmin, OwnershipRecordAdmin,
StateAdmin,
User, User,
UserAdmin, UserAdmin,
make_service_hooks_sync_nickname_action,
make_service_hooks_update_groups_action,
update_main_character_model,
user_main_organization, user_main_organization,
user_profile_pic, user_profile_pic,
user_username, user_username,
update_main_character_model,
make_service_hooks_update_groups_action,
make_service_hooks_sync_nickname_action
) )
from . import get_admin_change_view_url, get_admin_search_url from . import get_admin_change_view_url, get_admin_search_url
MODULE_PATH = 'allianceauth.authentication.admin' MODULE_PATH = 'allianceauth.authentication.admin'
@ -322,15 +327,15 @@ class TestUserAdmin(TestCaseWithTestData):
def test_user_username_u1(self): def test_user_username_u1(self):
expected = ( expected = (
f'<strong><a href="/admin/authentication/user/{self.user_1.pk}/change/">' '<strong><a href="/admin/authentication/user/{}/change/">'
'Bruce_Wayne</a></strong><br>Bruce Wayne' 'Bruce_Wayne</a></strong><br>Bruce Wayne'.format(self.user_1.pk)
) )
self.assertEqual(user_username(self.user_1), expected) self.assertEqual(user_username(self.user_1), expected)
def test_user_username_u3(self): def test_user_username_u3(self):
expected = ( expected = (
f'<strong><a href="/admin/authentication/user/{self.user_3.pk}/change/">' '<strong><a href="/admin/authentication/user/{}/change/">'
'Lex_Luthor</a></strong>' 'Lex_Luthor</a></strong>'.format(self.user_3.pk)
) )
self.assertEqual(user_username(self.user_3), expected) self.assertEqual(user_username(self.user_3), expected)

View File

@ -1,5 +1,4 @@
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from django.test import TestCase from django.test import TestCase
from .. import app_settings from .. import app_settings
@ -84,7 +83,7 @@ class TestSetAppSetting(TestCase):
self.assertEqual(result, 50) self.assertEqual(result, 50)
@patch(MODULE_PATH + '.app_settings.settings') @patch(MODULE_PATH + '.app_settings.settings')
def test_default_for_outofrange_int(self, mock_settings): def test_default_for_invalid_type_int(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = 1000 mock_settings.TEST_SETTING_DUMMY = 1000
result = app_settings._clean_setting( result = app_settings._clean_setting(
'TEST_SETTING_DUMMY', 'TEST_SETTING_DUMMY',
@ -97,7 +96,7 @@ class TestSetAppSetting(TestCase):
def test_default_is_none_needs_required_type(self, mock_settings): def test_default_is_none_needs_required_type(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = 'invalid type' mock_settings.TEST_SETTING_DUMMY = 'invalid type'
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
app_settings._clean_setting( result = app_settings._clean_setting(
'TEST_SETTING_DUMMY', 'TEST_SETTING_DUMMY',
default_value=None default_value=None
) )

View File

@ -1,13 +1,13 @@
from django.contrib.auth.models import Group, User from django.contrib.auth.models import User, Group
from django.test import TestCase from django.test import TestCase
from esi.models import Token
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from esi.models import Token
from ..backends import StateBackend from ..backends import StateBackend
from ..models import CharacterOwnership, OwnershipRecord, UserProfile from ..models import CharacterOwnership, UserProfile, OwnershipRecord
MODULE_PATH = 'allianceauth.authentication' MODULE_PATH = 'allianceauth.authentication'

View File

@ -6,11 +6,12 @@ from django.contrib.auth.models import AnonymousUser
from django.http.response import HttpResponse from django.http.response import HttpResponse
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.urls import URLPattern, reverse from django.urls import reverse, URLPattern
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from ..decorators import decorate_url_patterns, main_character_required from ..decorators import decorate_url_patterns, main_character_required
from ..models import CharacterOwnership from ..models import CharacterOwnership
@ -46,7 +47,7 @@ class DecoratorTestCase(TestCase):
@mock.patch(MODULE_PATH + '.decorators.messages') @mock.patch(MODULE_PATH + '.decorators.messages')
def test_login_redirect(self, m): def test_login_redirect(self, m):
self.request.user = AnonymousUser() setattr(self.request, 'user', AnonymousUser())
response = self.dummy_view(self.request) response = self.dummy_view(self.request)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
url = getattr(response, 'url', None) url = getattr(response, 'url', None)
@ -54,7 +55,7 @@ class DecoratorTestCase(TestCase):
@mock.patch(MODULE_PATH + '.decorators.messages') @mock.patch(MODULE_PATH + '.decorators.messages')
def test_main_character_redirect(self, m): def test_main_character_redirect(self, m):
self.request.user = self.no_main_user setattr(self.request, 'user', self.no_main_user)
response = self.dummy_view(self.request) response = self.dummy_view(self.request)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
url = getattr(response, 'url', None) url = getattr(response, 'url', None)
@ -62,7 +63,7 @@ class DecoratorTestCase(TestCase):
@mock.patch(MODULE_PATH + '.decorators.messages') @mock.patch(MODULE_PATH + '.decorators.messages')
def test_successful_request(self, m): def test_successful_request(self, m):
self.request.user = self.main_user setattr(self.request, 'user', self.main_user)
response = self.dummy_view(self.request) response = self.dummy_view(self.request)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)

View File

@ -1,9 +1,9 @@
from unittest.mock import Mock from unittest import mock
from django.http import HttpResponse
from django.test.testcases import TestCase
from allianceauth.authentication.middleware import UserSettingsMiddleware from allianceauth.authentication.middleware import UserSettingsMiddleware
from unittest.mock import Mock
from django.http import HttpResponse
from django.test.testcases import TestCase
class TestUserSettingsMiddlewareSaveLang(TestCase): class TestUserSettingsMiddlewareSaveLang(TestCase):
@ -39,7 +39,7 @@ class TestUserSettingsMiddlewareSaveLang(TestCase):
of a non-existent (anonymous) user of a non-existent (anonymous) user
""" """
self.request.user.is_anonymous = True self.request.user.is_anonymous = True
self.middleware.process_response( response = self.middleware.process_response(
self.request, self.request,
self.response self.response
) )
@ -52,7 +52,7 @@ class TestUserSettingsMiddlewareSaveLang(TestCase):
does the middleware change a language not set in the DB does the middleware change a language not set in the DB
""" """
self.request.user.profile.language = None self.request.user.profile.language = None
self.middleware.process_response( response = self.middleware.process_response(
self.request, self.request,
self.response self.response
) )
@ -64,7 +64,7 @@ class TestUserSettingsMiddlewareSaveLang(TestCase):
""" """
Tests the middleware will change a language setting Tests the middleware will change a language setting
""" """
self.middleware.process_response( response = self.middleware.process_response(
self.request, self.request,
self.response self.response
) )
@ -158,7 +158,7 @@ class TestUserSettingsMiddlewareLoginFlow(TestCase):
tests the middleware will set night_mode if not set tests the middleware will set night_mode if not set
""" """
self.request.session = {} self.request.session = {}
self.middleware.process_response( response = self.middleware.process_response(
self.request, self.request,
self.response self.response
) )
@ -168,7 +168,7 @@ class TestUserSettingsMiddlewareLoginFlow(TestCase):
""" """
tests the middleware will set night_mode if set. tests the middleware will set night_mode if set.
""" """
self.middleware.process_response( response = self.middleware.process_response(
self.request, self.request,
self.response self.response
) )

View File

@ -3,12 +3,12 @@ from unittest import mock
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
EveAllianceInfo, EveFactionInfo
from allianceauth.tests.auth_utils import AuthUtils
from esi.errors import IncompleteResponseError from esi.errors import IncompleteResponseError
from esi.models import Token from esi.models import Token
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo, EveFactionInfo
from allianceauth.tests.auth_utils import AuthUtils
from ..models import CharacterOwnership, State, get_guest_state from ..models import CharacterOwnership, State, get_guest_state
from ..tasks import check_character_ownership from ..tasks import check_character_ownership

View File

@ -1,10 +1,19 @@
from django.db.models.signals import post_save
from django.test.testcases import TestCase
from allianceauth.authentication.models import User, UserProfile from allianceauth.authentication.models import User, UserProfile
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo from allianceauth.eveonline.models import (
EveCharacter,
EveCorporationInfo,
EveAllianceInfo
)
from django.db.models.signals import (
pre_save,
post_save,
pre_delete,
m2m_changed
)
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from django.test.testcases import TestCase
from unittest.mock import Mock
from . import patch from . import patch

View File

@ -9,12 +9,8 @@ from django.core.cache import cache
from django.test import TestCase from django.test import TestCase
from allianceauth.templatetags.admin_status import ( from allianceauth.templatetags.admin_status import (
_current_notifications, _current_notifications, _current_version_summary, _fetch_list_from_gitlab,
_current_version_summary, _fetch_notification_issues_from_gitlab, _latests_versions, status_overview,
_fetch_list_from_gitlab,
_fetch_notification_issues_from_gitlab,
_latests_versions,
status_overview,
) )
MODULE_PATH = 'allianceauth.templatetags' MODULE_PATH = 'allianceauth.templatetags'
@ -131,7 +127,7 @@ class TestNotifications(TestCase):
# when # when
result = _current_notifications() result = _current_notifications()
# then # then
self.assertEqual(result['notifications'], []) self.assertEqual(result['notifications'], list())
@patch(MODULE_PATH + '.admin_status.cache') @patch(MODULE_PATH + '.admin_status.cache')
def test_current_notifications_is_none(self, mock_cache): def test_current_notifications_is_none(self, mock_cache):
@ -140,7 +136,7 @@ class TestNotifications(TestCase):
# when # when
result = _current_notifications() result = _current_notifications()
# then # then
self.assertEqual(result['notifications'], []) self.assertEqual(result['notifications'], list())
class TestCeleryQueueLength(TestCase): class TestCeleryQueueLength(TestCase):

View File

@ -1,13 +1,12 @@
import json import json
from unittest.mock import patch
import requests_mock import requests_mock
from unittest.mock import patch
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from allianceauth.authentication.constants import ESI_ERROR_MESSAGE_OVERRIDES from allianceauth.authentication.views import task_counts, esi_check
from allianceauth.authentication.views import esi_check, task_counts
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.authentication.constants import ESI_ERROR_MESSAGE_OVERRIDES
MODULE_PATH = "allianceauth.authentication.views" MODULE_PATH = "allianceauth.authentication.views"

View File

@ -38,6 +38,7 @@ urlpatterns = [
name='token_refresh' name='token_refresh'
), ),
path('dashboard/', views.dashboard, name='dashboard'), path('dashboard/', views.dashboard, name='dashboard'),
path('dashboard_bs3/', views.dashboard_bs3, name='dashboard_bs3'),
path('task-counts/', views.task_counts, name='task_counts'), path('task-counts/', views.task_counts, name='task_counts'),
path('esi-check/', views.esi_check, name='esi_check'), path('esi-check/', views.esi_check, name='esi_check'),
] ]

View File

@ -1,6 +1,11 @@
import logging import logging
import requests import requests
from django_registration.backends.activation.views import (
REGISTRATION_SALT, ActivationView as BaseActivationView,
RegistrationView as BaseRegistrationView,
)
from django_registration.signals import user_registered
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@ -13,12 +18,6 @@ from django.shortcuts import redirect, render
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_registration.backends.activation.views import (
REGISTRATION_SALT,
ActivationView as BaseActivationView,
RegistrationView as BaseRegistrationView,
)
from django_registration.signals import user_registered
from esi.decorators import token_required from esi.decorators import token_required
from esi.models import Token from esi.models import Token
@ -33,7 +32,7 @@ from .models import CharacterOwnership
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS: if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
_has_auto_groups = True _has_auto_groups = True
from allianceauth.eveonline.autogroups.models import * # noqa: F403 from allianceauth.eveonline.autogroups.models import * # noqa: F401, F403
else: else:
_has_auto_groups = False _has_auto_groups = False
@ -88,7 +87,7 @@ def dashboard_esi_check(request):
@login_required @login_required
def dashboard(request): def dashboard(request):
_dash_items = [] _dash_items = list()
hooks = get_hooks('dashboard_hook') hooks = get_hooks('dashboard_hook')
items = [fn() for fn in hooks] items = [fn() for fn in hooks]
items.sort(key=lambda i: i.order) items.sort(key=lambda i: i.order)
@ -165,7 +164,9 @@ def main_character_change(request, token):
request.user.profile.save(update_fields=['main_character']) request.user.profile.save(update_fields=['main_character'])
messages.success(request, _('Changed main character to %s') % co.character) messages.success(request, _('Changed main character to %s') % co.character)
logger.info( logger.info(
f'Changed user {request.user} main character to {co.character}' 'Changed user {user} main character to {char}'.format(
user=request.user, char=co.character
)
) )
return redirect("authentication:dashboard") return redirect("authentication:dashboard")
@ -175,9 +176,10 @@ def add_character(request, token):
if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter( if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter(
owner_hash=token.character_owner_hash).filter(user=request.user).exists(): owner_hash=token.character_owner_hash).filter(user=request.user).exists():
messages.success(request, _( messages.success(request, _(
f'Added {token.character_name} to your account.')) 'Added %(name)s to your account.' % ({'name': token.character_name})))
else: else:
messages.error(request, _(f'Failed to add {token.character_name} to your account: they already have an account.')) messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % (
{'name': token.character_name})))
return redirect('authentication:dashboard') return redirect('authentication:dashboard')
@ -292,7 +294,7 @@ class RegistrationView(BaseRegistrationView):
return redirect(settings.LOGIN_URL) return redirect(settings.LOGIN_URL)
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True): if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
# Keep the request so the user can be automagically logged in. # Keep the request so the user can be automagically logged in.
self.request = request setattr(self, 'request', request)
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def register(self, form): def register(self, form):
@ -392,3 +394,12 @@ def esi_check(request) -> JsonResponse:
"data": check_for_override_esi_error_message(_r) "data": check_for_override_esi_error_message(_r)
} }
return JsonResponse(data) return JsonResponse(data)
@login_required
def dashboard_bs3(request):
"""Render dashboard view with BS3 theme.
This is an internal view used for testing BS3 backward compatibility in AA4 only.
"""
return render(request, 'authentication/dashboard_bs3.html')

View File

@ -2,7 +2,6 @@
import os import os
import shutil import shutil
from optparse import OptionParser from optparse import OptionParser
from django.core.management import call_command from django.core.management import call_command
from django.core.management.commands.startproject import Command as BaseStartProject from django.core.management.commands.startproject import Command as BaseStartProject
@ -44,7 +43,7 @@ def create_project(parser, options, args):
# Call the command with extra context # Call the command with extra context
call_command(StartProject(), *args, **command_options) call_command(StartProject(), *args, **command_options)
print(f"Success! {args[0]} has been created.") print(f"Success! {args[0]} has been created.") # noqa
def update_settings(parser, options, args): def update_settings(parser, options, args):
@ -63,7 +62,7 @@ def update_settings(parser, options, args):
# next check if given path is to the project, so the app is within it # next check if given path is to the project, so the app is within it
settings_path = os.path.join(project_path, project_name, 'settings/base.py') settings_path = os.path.join(project_path, project_name, 'settings/base.py')
if not os.path.exists(settings_path): if not os.path.exists(settings_path):
parser.error(f"Unable to locate the Alliance Auth project at {project_path}") parser.error("Unable to locate the Alliance Auth project at %s" % project_path)
# first find the path to the Alliance Auth template settings # first find the path to the Alliance Auth template settings
import allianceauth import allianceauth

View File

@ -2,17 +2,15 @@
Django system checks for Alliance Auth Django system checks for Alliance Auth
""" """
from datetime import datetime, timezone from typing import List
from sqlite3.dbapi2 import sqlite_version_info
from celery import current_app
from packaging.version import InvalidVersion, Version as Pep440Version
from django import db from django import db
from django.conf import settings from django.core.checks import CheckMessage, Error, register, Warning
from django.core.checks import CheckMessage, Error, Warning, register
from allianceauth.utils.cache import get_redis_client 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
""" """
A = System Packages A = System Packages
@ -21,7 +19,7 @@ B = Configuration
@register() @register()
def django_settings(app_configs, **kwargs) -> list[CheckMessage]: def django_settings(app_configs, **kwargs) -> List[CheckMessage]:
""" """
Check that Django settings are correctly configured Check that Django settings are correctly configured
@ -33,7 +31,7 @@ def django_settings(app_configs, **kwargs) -> list[CheckMessage]:
:rtype: :rtype:
""" """
errors: list[CheckMessage] = [] errors: List[CheckMessage] = []
# Check for SITE_URL # Check for SITE_URL
if hasattr(settings, "SITE_URL"): if hasattr(settings, "SITE_URL"):
@ -111,7 +109,7 @@ def django_settings(app_configs, **kwargs) -> list[CheckMessage]:
@register() @register()
def system_package_redis(app_configs, **kwargs) -> list[CheckMessage]: def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]:
""" """
Check that Redis is a supported version Check that Redis is a supported version
@ -125,7 +123,7 @@ def system_package_redis(app_configs, **kwargs) -> list[CheckMessage]:
allianceauth_redis_install_link = "https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools" allianceauth_redis_install_link = "https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools"
errors: list[CheckMessage] = [] errors: List[CheckMessage] = []
try: try:
redis_version = Pep440Version(get_redis_client().info()["redis_version"]) redis_version = Pep440Version(get_redis_client().info()["redis_version"])
@ -137,8 +135,8 @@ def system_package_redis(app_configs, **kwargs) -> list[CheckMessage]:
if ( if (
redis_version.major == 7 redis_version.major == 7
and redis_version.minor == 2 and redis_version.minor == 2
and datetime.now(timezone.utc) and timezone.now()
> datetime(year=2025, month=8, day=31, tzinfo=timezone.utc) > timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc)
): ):
errors.append( errors.append(
Error( Error(
@ -176,7 +174,7 @@ def system_package_redis(app_configs, **kwargs) -> list[CheckMessage]:
@register() @register()
def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]: def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
""" """
Check that MySQL is a supported version Check that MySQL is a supported version
@ -190,7 +188,7 @@ def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]:
mysql_quick_guide_link = "https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/" mysql_quick_guide_link = "https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/"
errors: list[CheckMessage] = [] errors: List[CheckMessage] = []
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "mysql": if connection.vendor == "mysql":
@ -205,7 +203,7 @@ def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]:
# MySQL 8 # MySQL 8
if mysql_version.major == 8: if mysql_version.major == 8:
if mysql_version.minor == 4 and datetime.now(timezone.utc) > datetime( if mysql_version.minor == 4 and timezone.now() > timezone.datetime(
year=2032, month=4, day=30, tzinfo=timezone.utc year=2032, month=4, day=30, tzinfo=timezone.utc
): ):
errors.append( errors.append(
@ -215,8 +213,7 @@ def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]:
id="allianceauth.checks.A004", id="allianceauth.checks.A004",
) )
) )
# Demote versions down here once EOL elif mysql_version.minor == 3:
elif mysql_version.minor in [1, 2, 3]:
errors.append( errors.append(
Warning( Warning(
msg=f"MySQL {mysql_version.public} Non LTS", msg=f"MySQL {mysql_version.public} Non LTS",
@ -224,7 +221,23 @@ def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]:
id="allianceauth.checks.A005", id="allianceauth.checks.A005",
) )
) )
elif mysql_version.minor == 0 and datetime.now(timezone.utc) > datetime( elif mysql_version.minor == 2:
errors.append(
Warning(
msg=f"MySQL {mysql_version.public} Non LTS",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A006",
)
)
elif mysql_version.minor == 1:
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A007",
)
)
elif mysql_version.minor == 0 and timezone.now() > timezone.datetime(
year=2026, month=4, day=30, tzinfo=timezone.utc year=2026, month=4, day=30, tzinfo=timezone.utc
): ):
errors.append( errors.append(
@ -250,14 +263,21 @@ def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]:
@register() @register()
def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]: # noqa: C901 def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]:
""" """
Check that MariaDB is a supported version Check that MariaDB is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
""" """
mariadb_download_link = "https://mariadb.org/download/?t=repo-config" mariadb_download_link = "https://mariadb.org/download/?t=repo-config"
errors: list[CheckMessage] = [] errors: List[CheckMessage] = []
for connection in db.connections.all(): for connection in db.connections.all():
# TODO: Find a way to determine MySQL vs. MariaDB # TODO: Find a way to determine MySQL vs. MariaDB
@ -273,7 +293,7 @@ def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]: # noqa
# MariaDB 11 # MariaDB 11
if mariadb_version.major == 11: if mariadb_version.major == 11:
if mariadb_version.minor == 4 and datetime.now(timezone.utc) > datetime( if mariadb_version.minor == 4 and timezone.now() > timezone.datetime(
year=2029, month=5, day=19, tzinfo=timezone.utc year=2029, month=5, day=19, tzinfo=timezone.utc
): ):
errors.append( errors.append(
@ -283,8 +303,46 @@ def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]: # noqa
id="allianceauth.checks.A010", id="allianceauth.checks.A010",
) )
) )
elif mariadb_version.minor == 2:
errors.append(
Warning(
msg=f"MariaDB {mariadb_version.public} Non LTS",
hint=mariadb_download_link,
id="allianceauth.checks.A018",
)
)
if timezone.now() > timezone.datetime(
year=2024, month=11, day=21, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A011",
)
)
elif mariadb_version.minor == 1:
errors.append(
Warning(
msg=f"MariaDB {mariadb_version.public} Non LTS",
hint=mariadb_download_link,
id="allianceauth.checks.A019",
)
)
if timezone.now() > timezone.datetime(
year=2024, month=8, day=21, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A012",
)
)
# Demote versions down here once EOL # Demote versions down here once EOL
elif mariadb_version.minor in [0, 1, 2, 3, 5, 6]: elif mariadb_version.minor in [0, 3]:
errors.append( errors.append(
Error( Error(
msg=f"MariaDB {mariadb_version.public} EOL", msg=f"MariaDB {mariadb_version.public} EOL",
@ -295,7 +353,7 @@ def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]: # noqa
# MariaDB 10 # MariaDB 10
elif mariadb_version.major == 10: elif mariadb_version.major == 10:
if mariadb_version.minor == 11 and datetime.now(timezone.utc) > datetime( if mariadb_version.minor == 11 and timezone.now() > timezone.datetime(
year=2028, month=2, day=10, tzinfo=timezone.utc year=2028, month=2, day=10, tzinfo=timezone.utc
): ):
errors.append( errors.append(
@ -305,7 +363,7 @@ def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]: # noqa
id="allianceauth.checks.A014", id="allianceauth.checks.A014",
) )
) )
elif mariadb_version.minor == 6 and datetime.now(timezone.utc) > datetime( elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime(
year=2026, month=7, day=6, tzinfo=timezone.utc year=2026, month=7, day=6, tzinfo=timezone.utc
): ):
errors.append( errors.append(
@ -315,7 +373,7 @@ def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]: # noqa
id="allianceauth.checks.A0015", id="allianceauth.checks.A0015",
) )
) )
elif mariadb_version.minor == 5 and datetime.now(timezone.utc) > datetime( elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime(
year=2025, month=6, day=24, tzinfo=timezone.utc year=2025, month=6, day=24, tzinfo=timezone.utc
): ):
errors.append( errors.append(
@ -339,7 +397,7 @@ def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]: # noqa
@register() @register()
def system_package_sqlite(app_configs, **kwargs) -> list[CheckMessage]: def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]:
""" """
Check that SQLite is a supported version Check that SQLite is a supported version
@ -351,7 +409,7 @@ def system_package_sqlite(app_configs, **kwargs) -> list[CheckMessage]:
:rtype: :rtype:
""" """
errors: list[CheckMessage] = [] errors: List[CheckMessage] = []
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "sqlite": if connection.vendor == "sqlite":
@ -376,7 +434,7 @@ def system_package_sqlite(app_configs, **kwargs) -> list[CheckMessage]:
@register() @register()
def sql_settings(app_configs, **kwargs) -> list[CheckMessage]: def sql_settings(app_configs, **kwargs) -> List[CheckMessage]:
""" """
Check that SQL settings are correctly configured Check that SQL settings are correctly configured
@ -388,7 +446,7 @@ def sql_settings(app_configs, **kwargs) -> list[CheckMessage]:
:rtype: :rtype:
""" """
errors: list[CheckMessage] = [] errors: List[CheckMessage] = []
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "mysql": if connection.vendor == "mysql":
@ -438,7 +496,7 @@ def sql_settings(app_configs, **kwargs) -> list[CheckMessage]:
@register() @register()
def celery_settings(app_configs, **kwargs) -> list[CheckMessage]: def celery_settings(app_configs, **kwargs) -> List[CheckMessage]:
""" """
Check that Celery settings are correctly configured Check that Celery settings are correctly configured
@ -450,7 +508,7 @@ def celery_settings(app_configs, **kwargs) -> list[CheckMessage]:
:rtype: :rtype:
""" """
errors: list[CheckMessage] = [] errors: List[CheckMessage] = []
try: try:
if current_app.conf.broker_transport_options != { if current_app.conf.broker_transport_options != {

View File

@ -1,5 +1,4 @@
from django.conf import settings from django.conf import settings
from .views import NightModeRedirectView from .views import NightModeRedirectView

View File

@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from .models import CorpMember, CorpStats from .models import CorpStats, CorpMember
admin.site.register(CorpStats) admin.site.register(CorpStats)
admin.site.register(CorpMember) admin.site.register(CorpMember)

View File

@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class CorpUtilsConfig(AppConfig): class CorpUtilsConfig(AppConfig):
name = 'allianceauth.corputils' name = 'allianceauth.corputils'
label = 'corputils' label = 'corputils'
verbose_name = _('Corporation Stats')

View File

@ -1,9 +1,8 @@
from django.utils.translation import gettext_lazy as _
from allianceauth import hooks
from allianceauth.corputils import urls
from allianceauth.menu.hooks import MenuItemHook from allianceauth.menu.hooks import MenuItemHook
from allianceauth.services.hooks import UrlHook from allianceauth.services.hooks import UrlHook
from django.utils.translation import gettext_lazy as _
from allianceauth import hooks
from allianceauth.corputils import urls
class CorpStats(MenuItemHook): class CorpStats(MenuItemHook):

View File

@ -1,6 +1,5 @@
import logging
from django.db import models from django.db import models
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -9,7 +8,7 @@ class CorpStatsQuerySet(models.QuerySet):
def visible_to(self, user): def visible_to(self, user):
# superusers get all visible # superusers get all visible
if user.is_superuser: if user.is_superuser:
logger.debug(f'Returning all corpstats for superuser {user}.') logger.debug('Returning all corpstats for superuser %s.' % user)
return self return self
try: try:
@ -37,7 +36,7 @@ class CorpStatsQuerySet(models.QuerySet):
query |= q query |= q
return self.filter(query) return self.filter(query)
except AssertionError: except AssertionError:
logger.debug(f'User {user} has no main character. No corpstats visible.') logger.debug('User %s has no main character. No corpstats visible.' % user)
return self.none() return self.none()

View File

@ -1,7 +1,7 @@
# Generated by Django 1.10.1 on 2016-12-14 21:36 # Generated by Django 1.10.1 on 2016-12-14 21:36
import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -66,11 +66,11 @@ def forward(apps, schema_editor):
g.permissions.add(perm_dict['corpstats']['alliance_apis'].pk) g.permissions.add(perm_dict['corpstats']['alliance_apis'].pk)
g.permissions.add(perm_dict['corpstats']['view_alliance_corpstats'].pk) g.permissions.add(perm_dict['corpstats']['view_alliance_corpstats'].pk)
for _name, perm in perm_dict['user'].items(): for name, perm in perm_dict['user'].items():
perm.delete() perm.delete()
def reverse(apps, schema_editor): # noqa: C901 def reverse(apps, schema_editor):
perm_dict = user_permissions_dict(apps) perm_dict = user_permissions_dict(apps)
corp_users = users_with_permission(apps, perm_dict['corpstats']['view_corp_corpstats']) corp_users = users_with_permission(apps, perm_dict['corpstats']['view_corp_corpstats'])

View File

@ -1,9 +1,8 @@
# Generated by Django 1.10.5 on 2017-03-26 20:13 # Generated by Django 1.10.5 on 2017-03-26 20:13
import json
import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
import json
def convert_json_to_members(apps, schema_editor): def convert_json_to_members(apps, schema_editor):

View File

@ -1,18 +1,17 @@
import logging import logging
import os import os
from typing import ClassVar
from bravado.exception import HTTPForbidden
from django.db import models
from esi.errors import TokenError
from esi.models import Token
from allianceauth.authentication.models import CharacterOwnership, UserProfile from allianceauth.authentication.models import CharacterOwnership, UserProfile
from allianceauth.corputils.managers import CorpStatsManager from bravado.exception import HTTPForbidden
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo from django.db import models
from esi.errors import TokenError
from esi.models import Token
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter, EveAllianceInfo
from allianceauth.notifications import notify from allianceauth.notifications import notify
from allianceauth.corputils.managers import CorpStatsManager
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json') SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
""" """
Swagger spec operations: Swagger spec operations:
@ -33,7 +32,6 @@ class CorpStats(models.Model):
corp = models.OneToOneField(EveCorporationInfo, on_delete=models.CASCADE) corp = models.OneToOneField(EveCorporationInfo, on_delete=models.CASCADE)
last_update = models.DateTimeField(auto_now=True) last_update = models.DateTimeField(auto_now=True)
objects = CorpStatsManager()
class Meta: class Meta:
permissions = ( permissions = (
('view_corp_corpstats', 'Can view corp stats of their corporation.'), ('view_corp_corpstats', 'Can view corp stats of their corporation.'),
@ -43,7 +41,7 @@ class CorpStats(models.Model):
verbose_name = "corp stats" verbose_name = "corp stats"
verbose_name_plural = "corp stats" verbose_name_plural = "corp stats"
objects: ClassVar[CorpStatsManager] = CorpStatsManager()
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.__class__.__name__} for {self.corp}" return f"{self.__class__.__name__} for {self.corp}"
@ -79,21 +77,21 @@ class CorpStats(models.Model):
logger.warning(f"{self} failed to update: {e}") logger.warning(f"{self} failed to update: {e}")
if self.token.user: if self.token.user:
notify( notify(
self.token.user, f"{self} failed to update with your ESI token.", self.token.user, "%s failed to update with your ESI token." % self,
message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.", message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.",
level="error") level="error")
self.delete() self.delete()
except HTTPForbidden as e: except HTTPForbidden as e:
logger.warning(f"{self} failed to update: {e}") logger.warning(f"{self} failed to update: {e}")
if self.token.user: if self.token.user:
notify(self.token.user, f"{self} failed to update with your ESI token.", message=f"{e.status_code}: {e.message}", level="error") notify(self.token.user, "%s failed to update with your ESI token." % self, message=f"{e.status_code}: {e.message}", level="error")
self.delete() self.delete()
except AssertionError: except AssertionError:
logger.warning(f"{self} token character no longer in corp.") logger.warning("%s token character no longer in corp." % self)
if self.token.user: if self.token.user:
notify( notify(
self.token.user, f"{self} cannot update with your ESI token.", self.token.user, "%s cannot update with your ESI token." % self,
message=f"{self} cannot update with your ESI token as you have left corp.", level="error") message="%s cannot update with your ESI token as you have left corp." % self, level="error")
self.delete() self.delete()
@property @property
@ -154,7 +152,7 @@ class CorpMember(models.Model):
unique_together = ('corpstats', 'character_id') unique_together = ('corpstats', 'character_id')
ordering = ['character_name'] ordering = ['character_name']
def __str__(self) -> str: def __str__(self):
return self.character_name return self.character_name
@property @property

View File

@ -1,5 +1,4 @@
from celery import shared_task from celery import shared_task
from allianceauth.corputils.models import CorpStats from allianceauth.corputils.models import CorpStats

View File

@ -138,7 +138,7 @@
<td style="width: 30%;">{{ alt.corporation_name }}</td> <td style="width: 30%;">{{ alt.corporation_name }}</td>
<td style="width: 30%;">{{ alt.alliance_name|default_if_none:"" }}</td> <td style="width: 30%;">{{ alt.alliance_name|default_if_none:"" }}</td>
<td style="width: 5%;"> <td style="width: 5%;">
<a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="badge bg-danger" target="_blank"> <a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="badge text-bg-danger" target="_blank">
{% translate "Killboard" %} {% translate "Killboard" %}
</a> </a>
</td> </td>
@ -175,7 +175,7 @@
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member }}"></td> <td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member }}"></td>
<td>{{ member }}</td> <td>{{ member }}</td>
<td> <td>
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a> <a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge text-bg-danger" target="_blank">{% translate "Killboard" %}</a>
</td> </td>
<td>{{ member.character_ownership.user.profile.main_character.character_name }}</td> <td>{{ member.character_ownership.user.profile.main_character.character_name }}</td>
<td>{{ member.character_ownership.user.profile.main_character.corporation_name }}</td> <td>{{ member.character_ownership.user.profile.main_character.corporation_name }}</td>
@ -188,7 +188,7 @@
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td> <td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td>
<td>{{ member.character_name }}</td> <td>{{ member.character_name }}</td>
<td> <td>
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a> <a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge text-bg-danger" target="_blank">{% translate "Killboard" %}</a>
</td> </td>
<td></td> <td></td>
<td></td> <td></td>
@ -219,7 +219,7 @@
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td> <td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td>
<td>{{ member.character_name }}</td> <td>{{ member.character_name }}</td>
<td> <td>
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank"> <a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge text-bg-danger" target="_blank">
{% translate "Killboard" %} {% translate "Killboard" %}
</a> </a>
</td> </td>

View File

@ -28,7 +28,7 @@
<td><img src="{{ result.1.portrait_url }}" class="img-circle" alt="{{ result.1.character_name }}"></td> <td><img src="{{ result.1.portrait_url }}" class="img-circle" alt="{{ result.1.character_name }}"></td>
<td>{{ result.1.character_name }}</td> <td>{{ result.1.character_name }}</td>
<td >{{ result.0.corp.corporation_name }}</td> <td >{{ result.0.corp.corporation_name }}</td>
<td><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a></td> <td><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="badge text-bg-danger" target="_blank">{% translate "Killboard" %}</a></td>
<td>{{ result.1.main_character.character_name }}</td> <td>{{ result.1.main_character.character_name }}</td>
<td>{{ result.1.main_character.corporation_name }}</td> <td>{{ result.1.main_character.corporation_name }}</td>
<td>{{ result.1.main_character.alliance_name }}</td> <td>{{ result.1.main_character.alliance_name }}</td>

View File

@ -1,18 +1,14 @@
from unittest import mock from unittest import mock
from bravado.exception import HTTPForbidden
from django.contrib.auth.models import Permission
from django.test import TestCase from django.test import TestCase
from esi.errors import TokenError
from esi.models import Token
from allianceauth.authentication.models import CharacterOwnership
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from .models import CorpStats, CorpMember
from .models import CorpMember, CorpStats from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo, EveCharacter
from esi.models import Token
from esi.errors import TokenError
from bravado.exception import HTTPForbidden
from django.contrib.auth.models import Permission
from allianceauth.authentication.models import CharacterOwnership
class CorpStatsManagerTestCase(TestCase): class CorpStatsManagerTestCase(TestCase):

View File

@ -1,5 +1,4 @@
from django.urls import path from django.urls import path
from . import views from . import views
app_name = 'corputils' app_name = 'corputils'

View File

@ -1,19 +1,16 @@
import os import os
from bravado.exception import HTTPError from bravado.exception import HTTPError
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
from django.db import IntegrityError from django.db import IntegrityError
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from esi.decorators import token_required from esi.decorators import token_required
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo
from .models import CorpMember, CorpStats from .models import CorpStats, CorpMember
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json') SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
""" """
@ -62,7 +59,7 @@ def corpstats_add(request, token):
@login_required @login_required
@user_passes_test(access_corpstats_test) @user_passes_test(access_corpstats_test)
def corpstats_view(request, corp_id=None): # noqa: C901 def corpstats_view(request, corp_id=None):
corpstats = None corpstats = None
# get requested model # get requested model

View File

@ -3,6 +3,7 @@ Crontab App Config
""" """
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class CrontabConfig(AppConfig): class CrontabConfig(AppConfig):
@ -12,3 +13,4 @@ class CrontabConfig(AppConfig):
name = "allianceauth.crontab" name = "allianceauth.crontab"
label = "crontab" label = "crontab"
verbose_name = _("Crontab")

View File

@ -1,8 +1,7 @@
# Generated by Django 4.2.16 on 2025-01-20 06:16 # Generated by Django 4.2.16 on 2025-01-20 06:16
from django.db import migrations, models
import allianceauth.crontab.models import allianceauth.crontab.models
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -1,9 +1,7 @@
from random import random from random import random
from solo.models import SingletonModel
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from solo.models import SingletonModel
def random_default() -> float: def random_default() -> float:

View File

@ -1,10 +1,12 @@
from django.core.exceptions import ObjectDoesNotExist
from django_celery_beat.schedulers import (
DatabaseScheduler
)
from django_celery_beat.models import CrontabSchedule
from django.db.utils import OperationalError, ProgrammingError
from celery import schedules from celery import schedules
from celery.utils.log import get_logger from celery.utils.log import get_logger
from django_celery_beat.models import CrontabSchedule
from django_celery_beat.schedulers import DatabaseScheduler
from django.core.exceptions import ObjectDoesNotExist
from django.db.utils import OperationalError, ProgrammingError
from allianceauth.crontab.models import CronOffset from allianceauth.crontab.models import CronOffset
from allianceauth.crontab.utils import offset_cron from allianceauth.crontab.utils import offset_cron
@ -26,7 +28,7 @@ class OffsetDatabaseScheduler(DatabaseScheduler):
s = {} s = {}
try: try:
cron_offset = CronOffset.get_solo() # noqa: F841 cron_offset = CronOffset.get_solo()
except (OperationalError, ProgrammingError, ObjectDoesNotExist) as exc: except (OperationalError, ProgrammingError, ObjectDoesNotExist) as exc:
# This is just incase we haven't migrated yet or something # This is just incase we haven't migrated yet or something
logger.warning( logger.warning(

View File

@ -1,5 +1,4 @@
from unittest.mock import patch from unittest.mock import patch
from django.test import TestCase from django.test import TestCase
from allianceauth.crontab.models import CronOffset from allianceauth.crontab.models import CronOffset

View File

@ -2,14 +2,12 @@
import logging import logging
from unittest.mock import patch from unittest.mock import patch
from django.test import TestCase
from django.db import ProgrammingError
from celery.schedules import crontab from celery.schedules import crontab
from django.db import ProgrammingError
from django.test import TestCase
from allianceauth.crontab.models import CronOffset
from allianceauth.crontab.utils import offset_cron from allianceauth.crontab.utils import offset_cron
from allianceauth.crontab.models import CronOffset
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,10 +1,8 @@
import logging
from celery.schedules import crontab from celery.schedules import crontab
import logging
from allianceauth.crontab.models import CronOffset
from django.db import ProgrammingError from django.db import ProgrammingError
from allianceauth.crontab.models import CronOffset
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -3,15 +3,14 @@ Admin classes for custom_css app
""" """
# Django # Django
from django.contrib import admin
# Django Solos # Django Solos
from solo.admin import SingletonModelAdmin from solo.admin import SingletonModelAdmin
from django.contrib import admin
from allianceauth.custom_css.forms import CustomCSSAdminForm
# Alliance Auth Custom CSS # Alliance Auth Custom CSS
from allianceauth.custom_css.models import CustomCSS from allianceauth.custom_css.models import CustomCSS
from allianceauth.custom_css.forms import CustomCSSAdminForm
@admin.register(CustomCSS) @admin.register(CustomCSS)

View File

@ -3,12 +3,12 @@ Forms for custom_css app
""" """
# Alliance Auth Custom CSS # Alliance Auth Custom CSS
# Django
from django import forms
from allianceauth.custom_css.models import CustomCSS from allianceauth.custom_css.models import CustomCSS
from allianceauth.custom_css.widgets import CssEditorWidget from allianceauth.custom_css.widgets import CssEditorWidget
# Django
from django import forms
class CustomCSSAdminForm(forms.ModelForm): class CustomCSSAdminForm(forms.ModelForm):
""" """

View File

@ -21,6 +21,7 @@ class CustomCSS(SingletonModel):
css = models.TextField( css = models.TextField(
blank=True, blank=True,
null=True,
verbose_name=_("Your custom CSS"), verbose_name=_("Your custom CSS"),
help_text=_("This CSS will be added to the site after the default CSS."), help_text=_("This CSS will be added to the site after the default CSS."),
) )

View File

@ -3,7 +3,7 @@ Custom template tags for custom_css app
""" """
# Alliance Auth Custom CSS # Alliance Auth Custom CSS
from pathlib import Path from allianceauth.custom_css.models import CustomCSS
# Django # Django
from django.conf import settings from django.conf import settings
@ -11,7 +11,7 @@ from django.template.defaulttags import register
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from allianceauth.custom_css.models import CustomCSS from pathlib import Path
@register.simple_tag @register.simple_tag

View File

@ -6,6 +6,7 @@ Form widgets for custom_css app
from django import forms from django import forms
# Alliance Auth # Alliance Auth
from allianceauth.custom_css.models import CustomCSS
class CssEditorWidget(forms.Textarea): class CssEditorWidget(forms.Textarea):

View File

@ -1,10 +1,13 @@
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from .models import EveAllianceInfo, EveCharacter, EveCorporationInfo, EveFactionInfo
from .providers import ObjectNotFound from .providers import ObjectNotFound
from .models import EveAllianceInfo
from .models import EveCharacter
from .models import EveCorporationInfo
from .models import EveFactionInfo
class EveEntityExistsError(forms.ValidationError): class EveEntityExistsError(forms.ValidationError):
def __init__(self, entity_type_name, id): def __init__(self, entity_type_name, id):
@ -49,8 +52,8 @@ class EveFactionForm(EveEntityForm):
def clean_id(self): def clean_id(self):
try: try:
assert self.Meta.model.provider.get_faction(self.cleaned_data['id']) assert self.Meta.model.provider.get_faction(self.cleaned_data['id'])
except (AssertionError, ObjectNotFound) as e: except (AssertionError, ObjectNotFound):
raise EveEntityNotFoundError('faction', self.cleaned_data['id']) from e raise EveEntityNotFoundError('faction', self.cleaned_data['id'])
if self.Meta.model.objects.filter(faction_id=self.cleaned_data['id']).exists(): if self.Meta.model.objects.filter(faction_id=self.cleaned_data['id']).exists():
raise EveEntityExistsError('faction', self.cleaned_data['id']) raise EveEntityExistsError('faction', self.cleaned_data['id'])
return self.cleaned_data['id'] return self.cleaned_data['id']
@ -70,8 +73,8 @@ class EveCharacterForm(EveEntityForm):
def clean_id(self): def clean_id(self):
try: try:
assert self.Meta.model.provider.get_character(self.cleaned_data['id']) assert self.Meta.model.provider.get_character(self.cleaned_data['id'])
except (AssertionError, ObjectNotFound) as e: except (AssertionError, ObjectNotFound):
raise EveEntityNotFoundError(self.entity_type_name, self.cleaned_data['id']) from e raise EveEntityNotFoundError(self.entity_type_name, self.cleaned_data['id'])
if self.Meta.model.objects.filter(character_id=self.cleaned_data['id']).exists(): if self.Meta.model.objects.filter(character_id=self.cleaned_data['id']).exists():
raise EveEntityExistsError(self.entity_type_name, self.cleaned_data['id']) raise EveEntityExistsError(self.entity_type_name, self.cleaned_data['id'])
return self.cleaned_data['id'] return self.cleaned_data['id']
@ -90,8 +93,8 @@ class EveCorporationForm(EveEntityForm):
def clean_id(self): def clean_id(self):
try: try:
assert self.Meta.model.provider.get_corporation(self.cleaned_data['id']) assert self.Meta.model.provider.get_corporation(self.cleaned_data['id'])
except (AssertionError, ObjectNotFound) as e: except (AssertionError, ObjectNotFound):
raise EveEntityNotFoundError(self.entity_type_name, self.cleaned_data['id']) from e raise EveEntityNotFoundError(self.entity_type_name, self.cleaned_data['id'])
if self.Meta.model.objects.filter(corporation_id=self.cleaned_data['id']).exists(): if self.Meta.model.objects.filter(corporation_id=self.cleaned_data['id']).exists():
raise EveEntityExistsError(self.entity_type_name, self.cleaned_data['id']) raise EveEntityExistsError(self.entity_type_name, self.cleaned_data['id'])
return self.cleaned_data['id'] return self.cleaned_data['id']
@ -110,8 +113,8 @@ class EveAllianceForm(EveEntityForm):
def clean_id(self): def clean_id(self):
try: try:
assert self.Meta.model.provider.get_alliance(self.cleaned_data['id']) assert self.Meta.model.provider.get_alliance(self.cleaned_data['id'])
except (AssertionError, ObjectNotFound) as e: except (AssertionError, ObjectNotFound):
raise EveEntityNotFoundError(self.entity_type_name, self.cleaned_data['id']) from e raise EveEntityNotFoundError(self.entity_type_name, self.cleaned_data['id'])
if self.Meta.model.objects.filter(alliance_id=self.cleaned_data['id']).exists(): if self.Meta.model.objects.filter(alliance_id=self.cleaned_data['id']).exists():
raise EveEntityExistsError(self.entity_type_name, self.cleaned_data['id']) raise EveEntityExistsError(self.entity_type_name, self.cleaned_data['id'])
return self.cleaned_data['id'] return self.cleaned_data['id']

View File

@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class EveonlineConfig(AppConfig): class EveonlineConfig(AppConfig):
name = 'allianceauth.eveonline' name = 'allianceauth.eveonline'
label = 'eveonline' label = 'eveonline'
verbose_name = _('EVE Online')

View File

@ -1,9 +1,9 @@
import logging
from django.contrib import admin from django.contrib import admin
from django.db import models from django.db import models
from .models import AutogroupsConfig, ManagedCorpGroup, ManagedAllianceGroup
import logging
from .models import AutogroupsConfig, ManagedAllianceGroup, ManagedCorpGroup
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,9 +1,11 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class EveAutogroupsConfig(AppConfig): class EveAutogroupsConfig(AppConfig):
name = 'allianceauth.eveonline.autogroups' name = 'allianceauth.eveonline.autogroups'
label = 'eve_autogroups' label = 'eve_autogroups'
verbose_name = _('EVE Online Autogroups')
def ready(self): def ready(self):
pass import allianceauth.eveonline.autogroups.signals

Some files were not shown because too many files have changed in this diff Show More