Compare commits

..

113 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
Joel Falknau
055077fa77
Version Bump 4.6.4 2025-03-03 11:49:43 +10:00
Joel Falknau
f342ccbc6a
Revert "[REMOVE] Unused imports"
This reverts commit f497c18e5b5f6cd504ff57913a88beb5818ffdcb.
2025-03-03 11:47:19 +10:00
Joel Falknau
37ffd0a1ac
Version Bump 4.6.3 2025-03-03 11:29:26 +10:00
Ariel Rin
a1f705381e Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1687
2025-03-03 01:28:36 +00:00
Ariel Rin
c0970ad4fa Updates for project Alliance Auth 2025-03-03 01:28:36 +00:00
Ariel Rin
3818d0c6d1 Merge branch 'crontab' into 'master'
Crontab

See merge request allianceauth/allianceauth!1695
2025-03-03 01:19:32 +00:00
Ariel Rin
95411c79cb Crontab 2025-03-03 01:19:32 +00:00
Ariel Rin
eeccbbacfc Merge branch 'limit-slixmpp' into 'master'
[CHANGE] Limit `slixmpp` version

See merge request allianceauth/allianceauth!1694
2025-03-03 01:19:09 +00:00
Peter Pfeufer
f6c4180502
[CHANGE] Limit slixmpp version
An update to `slixmpp` v1.9.0 pulls in `pyasn1` v0.6.1, which causes a dependency issue with `python-jose`

> ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
> python-jose 3.4.0 requires pyasn1<0.5.0,>=0.4.1, but you have pyasn1 0.6.1 which is incompatible.
2025-02-28 11:09:22 +01:00
Ariel Rin
f4d3d6c0b1 Merge branch 'sorting-data' into 'master'
[ADD] Sorting data to the tables datetime fields

See merge request allianceauth/allianceauth!1691
2025-02-27 03:37:30 +00:00
Ariel Rin
e9d2d11297 Merge branch 'correct-locale-link' into 'master'
[FIX] Correct locale link for moment js

See merge request allianceauth/allianceauth!1692
2025-02-27 03:37:27 +00:00
Peter Pfeufer
96204b29e8
[REMOVE] Redundant locale information
Since momentJS is already loaded with `locale=True`, this is not needed anymore.
2025-02-26 22:57:19 +01:00
Peter Pfeufer
47842c1243
[FIX] Correct locale link for moment js
The other one was the directory for the node modules …
2025-02-26 21:53:27 +01:00
Peter Pfeufer
9b494106bc
[ADD] Sorting data to the table field
Since the localized date and time values can have different formats, sorting with them wasn't gonna work reliably, so we add the original datetime string as sorting data.
2025-02-26 21:33:21 +01:00
Ariel Rin
d51e730a7f Merge branch 'mumble-stats-date-fixes' into 'master'
[CHANGE] Datetime Improvements for Mumble Connection History

See merge request allianceauth/allianceauth!1689
2025-02-26 10:21:39 +00:00
Ariel Rin
363909c0c4 Merge branch 'remove-stray-a-tag' into 'master'
[REMOVE] Stray `a` tag from `header-nav-brand`

See merge request allianceauth/allianceauth!1688
2025-02-26 10:19:38 +00:00
Ariel Rin
82273f68fe Merge branch 'expand-checks' into 'master'
Expand checks

See merge request allianceauth/allianceauth!1685
2025-02-26 10:19:10 +00:00
Ariel Rin
12fa38b446 Merge branch 'lcode-links' into 'master'
[ADD] Links to available languages for mapping

See merge request allianceauth/allianceauth!1690
2025-02-26 10:18:31 +00:00
Peter Pfeufer
c26af593ff
[ADD] Links to available languaes for mapping
Makes it easier for adding new languages in the future
2025-02-26 11:11:18 +01:00
Peter Pfeufer
8e9a53c494
[CHANGE] Localze datetime output 2025-02-25 18:17:51 +01:00
Peter Pfeufer
5559ce5fbb
[CHANGE] Return empty field when no datetime available 2025-02-25 18:01:58 +01:00
Peter Pfeufer
faa529a55b
[REMOVE] Stray a tag from header-nav-brand 2025-02-25 17:33:02 +01:00
Peter Pfeufer
4ccfe20c14
[CHANGE] Set email to dummy@example.net 2025-02-25 11:52:25 +01:00
Peter Pfeufer
3acb651650
[CHANGE] Update test 2025-02-19 10:18:35 +01:00
Peter Pfeufer
2de57b334b
[ADD] ESI_USER_CONTACT_EMAIL to test settings 2025-02-19 10:09:00 +01:00
Peter Pfeufer
0498f5bb1b
[MISC] Format for better readability 2025-02-19 10:00:18 +01:00
Peter Pfeufer
929485a8f9
[ADD] Check for empty SITE_URL 2025-02-19 09:39:22 +01:00
Peter Pfeufer
28cb62f373
[ADD] Check for ESI_USER_CONTACT_EMAIL 2025-02-19 09:31:49 +01:00
158 changed files with 6311 additions and 4214 deletions

View File

@ -25,12 +25,12 @@ exclude: |
repos: repos:
# Code Upgrades # Code Upgrades
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v3.19.1 rev: v3.20.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py38-plus] args: [--py38-plus]
- repo: https://github.com/adamchainz/django-upgrade - repo: https://github.com/adamchainz/django-upgrade
rev: 1.22.2 rev: 1.25.0
hooks: hooks:
- id: django-upgrade - id: django-upgrade
args: [--target-version=4.2] args: [--target-version=4.2]
@ -66,11 +66,11 @@ repos:
- id: check-executables-have-shebangs - id: check-executables-have-shebangs
- id: end-of-file-fixer - id: end-of-file-fixer
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python - repo: https://github.com/editorconfig-checker/editorconfig-checker.python
rev: 3.2.0 rev: 3.2.1
hooks: hooks:
- id: editorconfig-checker - id: editorconfig-checker
- repo: https://github.com/igorshubovych/markdownlint-cli - repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.44.0 rev: v0.45.0
hooks: hooks:
- id: markdownlint - id: markdownlint
language: node language: node
@ -78,7 +78,7 @@ repos:
- --disable=MD013 - --disable=MD013
# Infrastructure # Infrastructure
- repo: https://github.com/tox-dev/pyproject-fmt - repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.5.0 rev: v2.6.0
hooks: hooks:
- id: pyproject-fmt - id: pyproject-fmt
name: pyproject.toml formatter name: pyproject.toml formatter
@ -88,7 +88,7 @@ repos:
additional_dependencies: additional_dependencies:
- tox==4.24.1 # https://github.com/tox-dev/tox/releases/latest - tox==4.24.1 # https://github.com/tox-dev/tox/releases/latest
- repo: https://github.com/abravalheri/validate-pyproject - repo: https://github.com/abravalheri/validate-pyproject
rev: v0.23 rev: v0.24.1
hooks: hooks:
- id: validate-pyproject - id: validate-pyproject
name: Validate pyproject.toml name: Validate pyproject.toml

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__ = '4.6.2a' __version__ = '4.8.0'
__title__ = 'Alliance Auth' __title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth' __url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = f'{__title__} v{__version__}' NAME = f'{__title__} v{__version__}'

View File

@ -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,6 +1,7 @@
from django.apps import apps from django.apps import apps
from allianceauth.authentication.models import User from allianceauth.authentication.models import User
from allianceauth.analytics.utils import install_stat_users, install_stat_addons 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 from django.test.testcases import TestCase

View File

@ -1,10 +1,12 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.core.checks import register, Tags 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,3 +1,6 @@
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 typing import Callable, Iterable, Optional

View File

@ -1,10 +1,12 @@
import logging import logging
from typing import ClassVar
from django.contrib.auth.models import User, Permission 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 EveCharacter, EveCorporationInfo, EveAllianceInfo, 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
@ -26,7 +28,7 @@ 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']
@ -136,8 +138,10 @@ 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): def __str__(self) -> str:
return str(self.user) return str(self.user)
class CharacterOwnership(models.Model): class CharacterOwnership(models.Model):
class Meta: class Meta:
default_permissions = ('change', 'delete') default_permissions = ('change', 'delete')
@ -147,7 +151,7 @@ class CharacterOwnership(models.Model):
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()
def __str__(self): def __str__(self):
return f"{self.user}: {self.character}" return f"{self.user}: {self.character}"

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,7 +1,6 @@
{% 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">
@ -13,5 +12,4 @@
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
</form> </form>
</div>

View File

@ -1,3 +1,9 @@
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 from unittest import mock

View File

@ -1,3 +1,4 @@
from unittest import mock
from allianceauth.authentication.middleware import UserSettingsMiddleware from allianceauth.authentication.middleware import UserSettingsMiddleware
from unittest.mock import Mock from unittest.mock import Mock
from django.http import HttpResponse from django.http import HttpResponse

View File

@ -4,10 +4,16 @@ from allianceauth.eveonline.models import (
EveCorporationInfo, EveCorporationInfo,
EveAllianceInfo EveAllianceInfo
) )
from django.db.models.signals import post_save 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 django.test.testcases import TestCase
from unittest.mock import Mock
from . import patch from . import patch

View File

@ -11,6 +11,7 @@ from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User
from django.core import signing from django.core import signing
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import redirect, render from django.shortcuts import redirect, render

View File

@ -1,3 +1,7 @@
"""
Django system checks for Alliance Auth
"""
from typing import List from typing import List
from django import db from django import db
from django.core.checks import CheckMessage, Error, register, Warning from django.core.checks import CheckMessage, Error, register, Warning
@ -16,48 +20,172 @@ B = Configuration
@register() @register()
def django_settings(app_configs, **kwargs) -> List[CheckMessage]: def django_settings(app_configs, **kwargs) -> List[CheckMessage]:
errors: List[CheckMessage] = [] """
if hasattr(settings, "SITE_URL"): Check that Django settings are correctly configured
if settings.SITE_URL[-1] == "/":
errors.append(Warning("'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth.", hint="", id="allianceauth.checks.B005"))
else:
errors.append(Error("No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.", hint="", id="allianceauth.checks.B006"))
if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"): :param app_configs:
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS: :type app_configs:
errors.append(Warning("'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.", hint="", id="allianceauth.checks.B007")) :param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = []
# Check for SITE_URL
if hasattr(settings, "SITE_URL"):
# Check if SITE_URL is empty
if settings.SITE_URL == "":
errors.append(
Error(
msg="'SITE_URL' is empty.",
hint="Make sure to set 'SITE_URL' to the URL of your Auth instance. (Without trailing slash)",
id="allianceauth.checks.B011",
)
)
# Check if SITE_URL has a trailing slash
elif settings.SITE_URL[-1] == "/":
errors.append(
Warning(
msg="'SITE_URL' has a trailing slash. This may lead to incorrect links being generated by Auth.",
hint="",
id="allianceauth.checks.B005",
)
)
# SITE_URL not found
else: else:
errors.append(Error("No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified", hint="", id="allianceauth.checks.B008")) errors.append(
Error(
msg="No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.",
hint="",
id="allianceauth.checks.B006",
)
)
# Check for CSRF_TRUSTED_ORIGINS
if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"):
# Check if SITE_URL is not in CSRF_TRUSTED_ORIGINS
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS:
errors.append(
Warning(
msg="'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.",
hint="",
id="allianceauth.checks.B007",
)
)
# CSRF_TRUSTED_ORIGINS not found
else:
errors.append(
Error(
msg="No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified",
hint="",
id="allianceauth.checks.B008",
)
)
# Check for ESI_USER_CONTACT_EMAIL
if hasattr(settings, "ESI_USER_CONTACT_EMAIL"):
# Check if ESI_USER_CONTACT_EMAIL is empty
if settings.ESI_USER_CONTACT_EMAIL == "":
errors.append(
Error(
msg="'ESI_USER_CONTACT_EMAIL' is empty. A valid email is required as maintainer contact for CCP.",
hint="",
id="allianceauth.checks.B009",
)
)
# ESI_USER_CONTACT_EMAIL not found
else:
errors.append(
Error(
msg="No 'ESI_USER_CONTACT_EMAIL' found is settings. A valid email is required as maintainer contact for CCP.",
hint="",
id="allianceauth.checks.B010",
)
)
return errors return errors
@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
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
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"])
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm Redis Version")) errors.append(Warning("Unable to confirm Redis Version"))
return errors return errors
if redis_version.major == 7 and redis_version.minor == 2 and timezone.now() > timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc): if (
errors.append(Error(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A001")) redis_version.major == 7
and redis_version.minor == 2
and timezone.now()
> timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc)
):
errors.append(
Error(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A001",
)
)
elif redis_version.major == 7 and redis_version.minor == 0: elif redis_version.major == 7 and redis_version.minor == 0:
errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A002")) errors.append(
Warning(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A002",
)
)
elif redis_version.major == 6 and redis_version.minor == 2: elif redis_version.major == 6 and redis_version.minor == 2:
errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A018")) errors.append(
Warning(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A018",
)
)
elif redis_version.major in [6, 5]: elif redis_version.major in [6, 5]:
errors.append(Error(f"Redis {redis_version.public} EOL", hint=allianceauth_redis_install_link, id="allianceauth.checks.A003")) errors.append(
Error(
msg=f"Redis {redis_version.public} EOL",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A003",
)
)
return errors return errors
@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
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
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] = []
@ -65,108 +193,302 @@ def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "mysql": if connection.vendor == "mysql":
try: try:
mysql_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) mysql_version = Pep440Version(
".".join(str(i) for i in connection.mysql_version)
)
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm MySQL Version")) errors.append(Warning("Unable to confirm MySQL Version"))
return errors return errors
# MySQL 8 # MySQL 8
if mysql_version.major == 8: if mysql_version.major == 8:
if mysql_version.minor == 4 and timezone.now() > timezone.datetime(year=2032, month=4, day=30, tzinfo=timezone.utc): if mysql_version.minor == 4 and timezone.now() > timezone.datetime(
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A004")) year=2032, month=4, day=30, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A004",
)
)
elif mysql_version.minor == 3: elif mysql_version.minor == 3:
errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint=mysql_quick_guide_link, id="allianceauth.checks.A005")) errors.append(
Warning(
msg=f"MySQL {mysql_version.public} Non LTS",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A005",
)
)
elif mysql_version.minor == 2: elif mysql_version.minor == 2:
errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint=mysql_quick_guide_link, id="allianceauth.checks.A006")) 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: elif mysql_version.minor == 1:
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A007")) errors.append(
elif mysql_version.minor == 0 and timezone.now() > timezone.datetime(year=2026, month=4, day=30, tzinfo=timezone.utc): Error(
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A008")) 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
):
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A008",
)
)
# MySQL below 8 # MySQL below 8
# This will also catch Mariadb 5.x # This will also catch Mariadb 5.x
elif mysql_version.major < 8: elif mysql_version.major < 8:
errors.append(Error(f"MySQL or MariaDB {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A009")) errors.append(
Error(
msg=f"MySQL or MariaDB {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A009",
)
)
return errors return errors
@register() @register()
def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]: def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]:
"""
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():
if connection.vendor == "mysql": # Still to find a way to determine MySQL vs MariaDB # TODO: Find a way to determine MySQL vs. MariaDB
if connection.vendor == "mysql":
try: try:
mariadb_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) mariadb_version = Pep440Version(
".".join(str(i) for i in connection.mysql_version)
)
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm MariaDB Version")) errors.append(Warning("Unable to confirm MariaDB Version"))
return errors return errors
# MariaDB 11 # MariaDB 11
if mariadb_version.major == 11: if mariadb_version.major == 11:
if mariadb_version.minor == 4 and timezone.now() > timezone.datetime(year=2029, month=5, day=19, tzinfo=timezone.utc): if mariadb_version.minor == 4 and timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A010")) year=2029, month=5, day=19, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A010",
)
)
elif mariadb_version.minor == 2: elif mariadb_version.minor == 2:
errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint=mariadb_download_link, id="allianceauth.checks.A018")) 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): if timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A011")) 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: elif mariadb_version.minor == 1:
errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint=mariadb_download_link, id="allianceauth.checks.A019")) 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): if timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A012")) year=2024, month=8, day=21, tzinfo=timezone.utc
elif mariadb_version.minor in [0, 3]: # Demote versions down here once EOL ):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A013")) errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A012",
)
)
# Demote versions down here once EOL
elif mariadb_version.minor in [0, 3]:
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A013",
)
)
# MariaDB 10 # MariaDB 10
elif mariadb_version.major == 10: elif mariadb_version.major == 10:
if mariadb_version.minor == 11 and timezone.now() > timezone.datetime(year=2028, month=2, day=10, tzinfo=timezone.utc): if mariadb_version.minor == 11 and timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A014")) year=2028, month=2, day=10, tzinfo=timezone.utc
elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime(year=2026, month=7, day=6, tzinfo=timezone.utc): ):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A0015")) errors.append(
elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime(year=2025, month=6, day=24, tzinfo=timezone.utc): Error(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A016")) msg=f"MariaDB {mariadb_version.public} EOL",
elif mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]: # Demote versions down here once EOL hint=mariadb_download_link,
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A017")) id="allianceauth.checks.A014",
)
)
elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime(
year=2026, month=7, day=6, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A0015",
)
)
elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime(
year=2025, month=6, day=24, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A016",
)
)
# Demote versions down here once EOL
elif mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]:
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A017",
)
)
return errors return errors
@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
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
: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":
try: try:
sqlite_version = Pep440Version(".".join(str(i) for i in sqlite_version_info)) sqlite_version = Pep440Version(
".".join(str(i) for i in sqlite_version_info)
)
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm SQLite Version")) errors.append(Warning("Unable to confirm SQLite Version"))
return errors return errors
if sqlite_version.major == 3 and sqlite_version.minor < 27: if sqlite_version.major == 3 and sqlite_version.minor < 27:
errors.append(Error(f"SQLite {sqlite_version.public} Unsupported by Django", hint="https://pkgs.org/download/sqlite3", id="allianceauth.checks.A020")) errors.append(
Error(
msg=f"SQLite {sqlite_version.public} Unsupported by Django",
hint="https://pkgs.org/download/sqlite3",
id="allianceauth.checks.A020",
)
)
return errors return errors
@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
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
: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":
try: try:
if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4": if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4":
errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) errors.append(
Error(
msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
id="allianceauth.checks.B001",
)
)
except KeyError: except KeyError:
errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) errors.append(
Error(
msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
id="allianceauth.checks.B001",
)
)
# This hasn't actually been set on AA yet # This hasn't actually been set on AA yet
# try: # try:
# if connection.settings_dict["OPTIONS"]["collation"] != "utf8mb4_unicode_ci": # if (
# errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) # connection.settings_dict["OPTIONS"]["collation"]
# != "utf8mb4_unicode_ci"
# ):
# errors.append(
# Error(
# msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}",
# hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
# id="allianceauth.checks.B001",
# )
# )
# except KeyError: # except KeyError:
# errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) # errors.append(
# Error(
# msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}",
# hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
# id="allianceauth.checks.B001",
# )
# )
# if connection.vendor == "sqlite": # if connection.vendor == "sqlite":
@ -175,19 +497,57 @@ 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
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = [] errors: List[CheckMessage] = []
try: try:
if current_app.conf.broker_transport_options != {'priority_steps': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'queue_order_strategy': 'priority'}: if current_app.conf.broker_transport_options != {
errors.append(Error("Celery Priorities are not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003")) "priority_steps": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"queue_order_strategy": "priority",
}:
errors.append(
Error(
msg="Celery Priorities are not set correctly",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc",
id="allianceauth.checks.B003",
)
)
except KeyError: except KeyError:
errors.append(Error("Celery Priorities are not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003")) errors.append(
Error(
msg="Celery Priorities are not set",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc",
id="allianceauth.checks.B003",
)
)
try: try:
if current_app.conf.broker_connection_retry_on_startup != True: if not current_app.conf.broker_connection_retry_on_startup:
errors.append(Error("Celery broker_connection_retry_on_startup not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004")) errors.append(
Error(
msg="Celery broker_connection_retry_on_startup not set correctly",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280",
id="allianceauth.checks.B004",
)
)
except KeyError: except KeyError:
errors.append(Error("Celery broker_connection_retry_on_startup not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004")) errors.append(
Error(
msg="Celery broker_connection_retry_on_startup not set",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280",
id="allianceauth.checks.B004",
)
)
return errors return errors

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,5 +1,6 @@
import logging import logging
import os import os
from typing import ClassVar
from allianceauth.authentication.models import CharacterOwnership, UserProfile from allianceauth.authentication.models import CharacterOwnership, UserProfile
from bravado.exception import HTTPForbidden from bravado.exception import HTTPForbidden
@ -40,9 +41,9 @@ class CorpStats(models.Model):
verbose_name = "corp stats" verbose_name = "corp stats"
verbose_name_plural = "corp stats" verbose_name_plural = "corp stats"
objects = CorpStatsManager() objects: ClassVar[CorpStatsManager] = CorpStatsManager()
def __str__(self): def __str__(self) -> str:
return f"{self.__class__.__name__} for {self.corp}" return f"{self.__class__.__name__} for {self.corp}"
def update(self): def update(self):

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

@ -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

@ -44,6 +44,7 @@ class OffsetDatabaseScheduler(DatabaseScheduler):
entry = self.Entry.from_entry(name, app=self.app, **entry_fields) entry = self.Entry.from_entry(name, app=self.app, **entry_fields)
if apply_offset: if apply_offset:
entry_fields.update({"apply_offset": apply_offset}) # Reapply this as its gets pulled from config inconsistently.
schedule_obj = entry.schedule schedule_obj = entry.schedule
if isinstance(schedule_obj, schedules.crontab): if isinstance(schedule_obj, schedules.crontab):
offset_cs = CrontabSchedule.from_schedule(offset_cron(schedule_obj)) offset_cs = CrontabSchedule.from_schedule(offset_cron(schedule_obj))
@ -55,6 +56,7 @@ class OffsetDatabaseScheduler(DatabaseScheduler):
day_of_week=offset_cs.day_of_week, day_of_week=offset_cs.day_of_week,
timezone=offset_cs.timezone, timezone=offset_cs.timezone,
) )
entry.schedule = offset_cron(schedule_obj) # This gets passed into Celery Beats Memory, important to keep it in sync with the model/DB
entry.model.crontab = offset_cs entry.model.crontab = offset_cs
entry.model.save() entry.model.save()
logger.debug(f"Offset applied for '{name}' due to 'apply_offset' = True.") logger.debug(f"Offset applied for '{name}' due to 'apply_offset' = True.")

View File

@ -1,3 +1,5 @@
# myapp/tests/test_tasks.py
import logging import logging
from unittest.mock import patch from unittest.mock import patch
from django.test import TestCase from django.test import TestCase

View File

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

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,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):
import allianceauth.eveonline.autogroups.signals import allianceauth.eveonline.autogroups.signals

View File

@ -1,4 +1,5 @@
import logging import logging
from typing import ClassVar
from django.db import models, transaction from django.db import models, transaction
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@ -38,13 +39,13 @@ class AutogroupsConfigManager(models.Manager):
""" """
if state is None: if state is None:
state = user.profile.state state = user.profile.state
for config in self.filter(states=state):
# grant user new groups for their state
config.update_group_membership_for_user(user)
for config in self.exclude(states=state): for config in self.exclude(states=state):
# ensure user does not have groups from previous state # ensure user does not have groups from previous state
config.remove_user_from_alliance_groups(user) config.remove_user_from_alliance_groups(user)
config.remove_user_from_corp_groups(user) config.remove_user_from_corp_groups(user)
for config in self.filter(states=state):
# grant user new groups for their state
config.update_group_membership_for_user(user)
class AutogroupsConfig(models.Model): class AutogroupsConfig(models.Model):
@ -78,7 +79,7 @@ class AutogroupsConfig(models.Model):
max_length=10, default='', blank=True, max_length=10, default='', blank=True,
help_text='Any spaces in the group name will be replaced with this.') help_text='Any spaces in the group name will be replaced with this.')
objects = AutogroupsConfigManager() objects: ClassVar[AutogroupsConfigManager] = AutogroupsConfigManager()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@ -1,3 +1,4 @@
from allianceauth.eveonline.models import EveCorporationInfo
from django.test import TestCase from django.test import TestCase
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
@ -73,3 +74,51 @@ class AutogroupsConfigManagerTestCase(TestCase):
AutogroupsConfig.objects.update_groups_for_user(member) AutogroupsConfig.objects.update_groups_for_user(member)
self.assertTrue(update_groups.called) self.assertTrue(update_groups.called)
def test_update_group_membership_corp_in_two_configs(self):
# given
member = AuthUtils.create_member('test member')
AuthUtils.add_main_character_2(
member,
character_id='1234',
name='test character',
corp_id='2345',
corp_name='corp name',
corp_ticker='TIKK',
)
corp = EveCorporationInfo.objects.create(
corporation_id='2345',
corporation_name='corp name',
corporation_ticker='TIKK',
member_count=10,
)
member_state = AuthUtils.get_member_state()
member_config = AutogroupsConfig.objects.create(corp_groups=True)
member_config.states.add(member_state)
blue_state = AuthUtils.get_blue_state()
blue_state.member_corporations.add(corp)
blue_config = AutogroupsConfig.objects.create(corp_groups=True)
blue_config.states.add(blue_state)
member.profile.state = blue_state
member.profile.save()
AutogroupsConfig.objects.update_groups_for_user(member)
# Checks before test that the role is correctly applied
group = blue_config.get_corp_group(corp)
self.assertIn(group, member.groups.all())
# when
blue_state.member_corporations.remove(corp)
member_state.member_corporations.add(corp)
member.profile.state = member_state
member.profile.save()
# then
AutogroupsConfig.objects.update_groups_for_user(member)
group = member_config.get_corp_group(corp)
self.assertIn(group, member.groups.all())

View File

@ -1,5 +1,6 @@
from django.test import TestCase from django.test import TestCase
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.db import transaction
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils

View File

@ -1,6 +1,8 @@
from django.test import TestCase from django.test import TestCase
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
from .. import dotlan, zkillboard, evewho, eveimageserver from .. import dotlan, zkillboard, evewho, eveimageserver
from ...templatetags import evelinks
class TestEveWho(TestCase): class TestEveWho(TestCase):

View File

@ -14,10 +14,20 @@ class EveCharacterProviderManager:
class EveCharacterManager(models.Manager): class EveCharacterManager(models.Manager):
provider = EveCharacterProviderManager() provider = EveCharacterProviderManager()
def create_character(self, character_id): def exclude_biomassed(self):
"""
Get a queryset of EveCharacter objects, excluding the "Doomheim" corporation (1000001).
:return:
:rtype:
"""
return self.exclude(corporation_id=1000001)
def create_character(self, character_id) -> models.Model:
return self.create_character_obj(self.provider.get_character(character_id)) return self.create_character_obj(self.provider.get_character(character_id))
def create_character_obj(self, character: providers.Character): def create_character_obj(self, character: providers.Character) -> models.Model:
return self.create( return self.create(
character_id=character.id, character_id=character.id,
character_name=character.name, character_name=character.name,

View File

@ -1,5 +1,5 @@
import logging import logging
from typing import Union from typing import ClassVar, Union
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
@ -75,8 +75,8 @@ class EveAllianceInfo(models.Model):
alliance_ticker = models.CharField(max_length=254) alliance_ticker = models.CharField(max_length=254)
executor_corp_id = models.PositiveIntegerField() executor_corp_id = models.PositiveIntegerField()
objects = EveAllianceManager() objects: ClassVar[EveAllianceManager] = EveAllianceManager()
provider = EveAllianceProviderManager() provider: ClassVar[EveAllianceProviderManager] = EveAllianceProviderManager()
class Meta: class Meta:
indexes = [models.Index(fields=['executor_corp_id',])] indexes = [models.Index(fields=['executor_corp_id',])]
@ -147,7 +147,7 @@ class EveCorporationInfo(models.Model):
EveAllianceInfo, blank=True, null=True, on_delete=models.SET_NULL EveAllianceInfo, blank=True, null=True, on_delete=models.SET_NULL
) )
objects = EveCorporationManager() objects: ClassVar[EveCorporationManager] = EveCorporationManager()
provider = EveCorporationProviderManager() provider = EveCorporationProviderManager()
class Meta: class Meta:
@ -214,7 +214,7 @@ class EveCharacter(models.Model):
faction_id = models.PositiveIntegerField(blank=True, null=True, default=None) faction_id = models.PositiveIntegerField(blank=True, null=True, default=None)
faction_name = models.CharField(max_length=254, blank=True, null=True, default='') faction_name = models.CharField(max_length=254, blank=True, null=True, default='')
objects = EveCharacterManager() objects: ClassVar[EveCharacterManager] = EveCharacterManager()
provider = EveCharacterProviderManager() provider = EveCharacterProviderManager()
class Meta: class Meta:

View File

@ -723,5 +723,5 @@ class TestEveSwaggerProvider(TestCase):
my_client = my_provider.client my_client = my_provider.client
operation = my_client.Universe.get_universe_factions() operation = my_client.Universe.get_universe_factions()
self.assertEqual( self.assertEqual(
operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0' operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0 dummy@example.net'
) )

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 FatConfig(AppConfig): class FatConfig(AppConfig):
name = 'allianceauth.fleetactivitytracking' name = 'allianceauth.fleetactivitytracking'
label = 'fleetactivitytracking' label = 'fleetactivitytracking'
verbose_name = _('Fleet Activity Tracking')

View File

@ -36,7 +36,7 @@
<th class="text-center">{% translate "Character" %}</th> <th class="text-center">{% translate "Character" %}</th>
<th class="text-center">{% translate "System" %}</th> <th class="text-center">{% translate "System" %}</th>
<th class="text-center">{% translate "Ship" %}</th> <th class="text-center">{% translate "Ship" %}</th>
<th class="text-center">{% translate "Eve Time" %}</th> <th class="text-center">{% translate "EVE time" %}</th>
<th></th> <th></th>
</tr> </tr>

View File

@ -72,7 +72,7 @@
<tr> <tr>
<th class="text-center">{% translate "Fleet" %}</th> <th class="text-center">{% translate "Fleet" %}</th>
<th class="text-center">{% translate "Creator" %}</th> <th class="text-center">{% translate "Creator" %}</th>
<th class="text-center">{% translate "Eve Time" %}</th> <th class="text-center">{% translate "EVE time" %}</th>
<th class="text-center">{% translate "Duration" %}</th> <th class="text-center">{% translate "Duration" %}</th>
<th class="text-center">{% translate "Edit" %}</th> <th class="text-center">{% translate "Edit" %}</th>
</tr> </tr>
@ -80,7 +80,7 @@
{% for link in created_fats %} {% for link in created_fats %}
<tr> <tr>
<td class="text-center"> <td class="text-center">
<a href="{% url 'fatlink:click' link.hash %}" class="badge bg-primary"> <a href="{% url 'fatlink:click' link.hash %}" class="badge text-bg-primary">
{{ link.fleet }} {{ link.fleet }}
</a> </a>
</td> </td>

View File

@ -41,7 +41,7 @@
<th scope="col" class="text-center">{% translate "Character" %}</th> <th scope="col" class="text-center">{% translate "Character" %}</th>
<th scope="col" class="text-center">{% translate "System" %}</th> <th scope="col" class="text-center">{% translate "System" %}</th>
<th scope="col" class="text-center">{% translate "Ship" %}</th> <th scope="col" class="text-center">{% translate "Ship" %}</th>
<th scope="col" class="text-center">{% translate "Eve Time" %}</th> <th scope="col" class="text-center">{% translate "EVE time" %}</th>
</tr> </tr>
{% for fat in fats %} {% for fat in fats %}
@ -89,7 +89,7 @@
<th scope="col" class="text-center">{% translate "Name" %}</th> <th scope="col" class="text-center">{% translate "Name" %}</th>
<th scope="col" class="text-center">{% translate "Creator" %}</th> <th scope="col" class="text-center">{% translate "Creator" %}</th>
<th scope="col" class="text-center">{% translate "Fleet" %}</th> <th scope="col" class="text-center">{% translate "Fleet" %}</th>
<th scope="col" class="text-center">{% translate "Eve Time" %}</th> <th scope="col" class="text-center">{% translate "EVE time" %}</th>
<th scope="col" class="text-center">{% translate "Duration" %}</th> <th scope="col" class="text-center">{% translate "Duration" %}</th>
<th scope="col" class="text-center">{% translate "Edit" %}</th> <th scope="col" class="text-center">{% translate "Edit" %}</th>
</tr> </tr>
@ -97,7 +97,7 @@
{% for link in fatlinks %} {% for link in fatlinks %}
<tr> <tr>
<td class="text-center"> <td class="text-center">
<a href="{% url 'fatlink:click' link.hash %}" class="badge bg-primary">{{ link.fleet }}</a> <a href="{% url 'fatlink:click' link.hash %}" class="badge text-bg-primary">{{ link.fleet }}</a>
</td> </td>
<td class="text-center">{{ link.creator.username }}</td> <td class="text-center">{{ link.creator.username }}</td>
<td class="text-center">{{ link.fleet }}</td> <td class="text-center">{{ link.fleet }}</td>

View File

@ -8,7 +8,7 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404, Http404
from django.utils import timezone from django.utils import timezone
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

View File

@ -10,23 +10,38 @@ from allianceauth.authentication.models import CharacterOwnership
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter
def get_all_characters_from_user(user: User) -> list: def get_all_characters_from_user(user: User, main_first: bool = False) -> list:
""" """
Get all characters from a user or an empty list Get all characters from a user
when no characters are found for the user or the user is None This function retrieves all characters associated with a given user, optionally ordering them
with the main character first.
If the user is None, an empty list is returned.
:param user: :param user: The user whose characters are to be retrieved
:type user: :type user: User
:return: :param main_first: If True, the main character will be listed first
:rtype: :type main_first: bool
:return: A list of EveCharacter objects associated with the user
:rtype: list[EveCharacter]
""" """
if user is None: if user is None:
return [] return []
try: try:
if main_first:
characters = [ characters = [
char.character for char in CharacterOwnership.objects.filter(user=user) char.character
for char in CharacterOwnership.objects.filter(user=user).order_by(
"-character__userprofile", "character__character_name"
)
]
else:
characters = [
char.character
for char in CharacterOwnership.objects.filter(user=user).order_by(
"character__character_name"
)
] ]
except AttributeError: except AttributeError:
return [] return []

View File

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

View File

@ -5,6 +5,24 @@
* to be used throughout Alliance Auth and its Community Apps * to be used throughout Alliance Auth and its Community Apps
*/ */
/* General
------------------------------------------------------------------------------------- */
@media all {
.navbar-toggler.collapsed {
transform: rotate(180deg);
}
ul#nav-right:has(li) + ul#nav-right-character-control > li:first-child {
display: list-item !important;
}
}
@media all and (max-width: 991px) {
ul#nav-left:has(li) + ul#nav-right + ul#nav-right-character-control > li:first-child {
display: list-item !important;
}
}
/* Bootstrap fixes /* Bootstrap fixes
------------------------------------------------------------------------------------- */ ------------------------------------------------------------------------------------- */
@media all { @media all {

View File

@ -0,0 +1,105 @@
"""
Custom static files storage for Alliance Auth.
This module defines a custom static files storage class for
Alliance Auth, named `AaManifestStaticFilesStorage`.
Using `ManifestStaticFilesStorage` will give us a hashed name for
our static files, which is useful for cache busting.
This storage class extends Django's `ManifestStaticFilesStorage` to ignore missing files,
which the original class does not handle, and log them in debug mode.
It is useful for handling cases where static files may not exist, such as when a
CSS file references a background image that is not present in the static files directory.
With debug mode enabled, it will print a message for each missing file when running `collectstatic`,
which can help identify issues with static file references during development.
"""
from django.conf import settings
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
class AaManifestStaticFilesStorage(ManifestStaticFilesStorage):
"""
Custom static files storage that ignores missing files.
"""
@classmethod
def _cleanup_name(cls, name: str) -> str:
"""
Clean up the name by removing quotes.
This method is used to ensure that the name does not contain any quotes,
which can cause issues with file paths.
:param name: The name of the static file.
:type name: str
:return: The cleaned-up name without quotes.
:rtype: str
"""
# Remove quotes from the name
return name.replace('"', "").replace("'", "")
def __init__(self, *args, **kwargs):
"""
Initialize the static files storage, ignoring missing files.
:param args:
:type args:
:param kwargs:
:type kwargs:
"""
self.missing_files = []
super().__init__(*args, **kwargs)
def hashed_name(self, name, content=None, filename=None):
"""
Generate a hashed name for the given static file, ignoring missing files.
Ignore missing files, e.g. non-existent background image referenced from css.
Returns the original filename if the referenced file doesn't exist.
:param name: The name of the static file to hash.
:type name: str
:param content: The content of the static file, if available.
:type content: bytes | None
:param filename: The original filename of the static file, if available.
:type filename: str | None
:return: The hashed name of the static file, or the original name if the file is missing.
:rtype: str
"""
try:
clean_name = self._cleanup_name(name)
return super().hashed_name(clean_name, content, filename)
except ValueError as e:
if settings.DEBUG:
# In debug mode, we log the missing file message
message = e.args[0].split(" with ")[0]
self.missing_files.append(message)
# print(f'\x1b[0;30;41m{message}\x1b[0m')
return name
def post_process(self, *args, **kwargs):
"""
Post-process the static files, printing any missing files in debug mode.
:param args:
:type args:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
yield from super().post_process(*args, **kwargs)
if settings.DEBUG:
# In debug mode, print the missing files
for message in sorted(set(self.missing_files)):
print(f"\x1b[0;30;41m{message}\x1b[0m")

View File

@ -2,7 +2,8 @@
{# {% include "framework/header/page-header.html" with title="Foobar" subtitle="Barfoo" %}#} {# {% include "framework/header/page-header.html" with title="Foobar" subtitle="Barfoo" %}#}
{% if title %} {% if title %}
<h1 class="page-header text-center mb-3"> <header class="aa-page-header mb-3">
<h1 class="page-header text-center">
{{ title }} {{ title }}
{% if subtitle %} {% if subtitle %}
@ -10,4 +11,5 @@
<small class="text-muted">{{ subtitle }}</small> <small class="text-muted">{{ subtitle }}</small>
{% endif %} {% endif %}
</h1> </h1>
</header>
{% endif %} {% endif %}

View File

@ -1,10 +1,11 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class GroupManagementConfig(AppConfig): class GroupManagementConfig(AppConfig):
name = 'allianceauth.groupmanagement' name = 'allianceauth.groupmanagement'
label = 'groupmanagement' label = 'groupmanagement'
verbose_name = 'Group Management' verbose_name = _('Group Management')
def ready(self): def ready(self):
from . import signals # noqa: F401 from . import signals # noqa: F401

View File

@ -39,12 +39,12 @@
<td> <td>
{% if group.authgroup.hidden %} {% if group.authgroup.hidden %}
<span class="badge bg-info">{% translate "Hidden" %}</span> <span class="badge text-bg-info">{% translate "Hidden" %}</span>
{% endif %} {% endif %}
{% if group.authgroup.open %} {% if group.authgroup.open %}
<span class="badge bg-success">{% translate "Open" %}</span> <span class="badge text-bg-success">{% translate "Open" %}</span>
{% else %} {% else %}
<span class="badge bg-secondary">{% translate "Requestable" %}</span> <span class="badge text-bg-secondary">{% translate "Requestable" %}</span>
{% endif %} {% endif %}
</td> </td>

View File

@ -17,7 +17,7 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{% url 'groupmanagement:management' %}">{% translate "Group Management" %} <a class="nav-link" href="{% url 'groupmanagement:management' %}">{% translate "Group Management" %}
{% if req_count %} {% if req_count %}
<span class="badge bg-secondary">{{ req_count }}</span> <span class="badge text-bg-secondary">{{ req_count }}</span>
{% endif %} {% endif %}
</a> </a>
</li> </li>
@ -33,8 +33,8 @@
<th>{% translate "Description" %}</th> <th>{% translate "Description" %}</th>
<th> <th>
{% translate "Leaders" %}<br> {% translate "Leaders" %}<br>
<span class="my-1 me-1 fw-lighter badge bg-primary">{% translate "User" %}</span> <span class="my-1 me-1 fw-lighter badge text-bg-primary">{% translate "User" %}</span>
<span class="my-1 me-1 fw-lighter badge bg-secondary">{% translate "Group" %}</span> <span class="my-1 me-1 fw-lighter badge text-bg-secondary">{% translate "Group" %}</span>
</th> </th>
<th></th> <th></th>
</tr> </tr>
@ -53,13 +53,13 @@
{% if g.group.authgroup.group_leaders.all.count %} {% if g.group.authgroup.group_leaders.all.count %}
{% for leader in g.group.authgroup.group_leaders.all %} {% for leader in g.group.authgroup.group_leaders.all %}
{% if leader.profile.main_character %} {% if leader.profile.main_character %}
<span class="my-1 me-1 badge bg-primary">{{leader.profile.main_character}}</span> <span class="my-1 me-1 badge text-bg-primary">{{leader.profile.main_character}}</span>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if g.group.authgroup.group_leader_groups.all.count %} {% if g.group.authgroup.group_leader_groups.all.count %}
{% for group in g.group.authgroup.group_leader_groups.all %} {% for group in g.group.authgroup.group_leader_groups.all %}
<span class="my-1 me-1 badge bg-secondary">{{group.name}}</span> <span class="my-1 me-1 badge text-bg-secondary">{{group.name}}</span>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</td> </td>

View File

@ -19,7 +19,7 @@
{% translate "Join Requests" %} {% translate "Join Requests" %}
{% if acceptrequests %} {% if acceptrequests %}
<span class="badge bg-secondary">{{ acceptrequests|length }}</span> <span class="badge text-bg-secondary">{{ acceptrequests|length }}</span>
{% endif %} {% endif %}
</a> </a>
</li> </li>
@ -30,7 +30,7 @@
{% translate "Leave Requests" %} {% translate "Leave Requests" %}
{% if leaverequests %} {% if leaverequests %}
<span class="badge bg-secondary">{{ leaverequests|length }}</span> <span class="badge text-bg-secondary">{{ leaverequests|length }}</span>
{% endif %} {% endif %}
</a> </a>
</li> </li>

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 HRApplicationsConfig(AppConfig): class HRApplicationsConfig(AppConfig):
name = 'allianceauth.hrapplications' name = 'allianceauth.hrapplications'
label = 'hrapplications' label = 'hrapplications'
verbose_name = _('HR Applications')

View File

@ -1,3 +1,4 @@
from typing import ClassVar
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from sortedm2m.fields import SortedManyToManyField from sortedm2m.fields import SortedManyToManyField
@ -40,7 +41,7 @@ class Application(models.Model):
reviewer_character = models.ForeignKey(EveCharacter, on_delete=models.SET_NULL, blank=True, null=True) reviewer_character = models.ForeignKey(EveCharacter, on_delete=models.SET_NULL, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
objects = ApplicationManager() objects: ClassVar[ApplicationManager] = ApplicationManager()
def __str__(self): def __str__(self):
return str(self.user) + " Application To " + str(self.form) return str(self.user) + " Application To " + str(self.form)

View File

@ -43,11 +43,11 @@
<td class="text-center">{{ personal_app.form.corp.corporation_name }}</td> <td class="text-center">{{ personal_app.form.corp.corporation_name }}</td>
<td class="text-center"> <td class="text-center">
{% if personal_app.approved == None %} {% if personal_app.approved == None %}
<div class="badge bg-warning">{% translate "Pending" %}</div> <div class="badge text-bg-warning">{% translate "Pending" %}</div>
{% elif personal_app.approved == True %} {% elif personal_app.approved == True %}
<div class="badge bg-success">{% translate "Approved" %}</div> <div class="badge text-bg-success">{% translate "Approved" %}</div>
{% else %} {% else %}
<div class="badge bg-danger">{% translate "Rejected" %}</div> <div class="badge text-bg-danger">{% translate "Rejected" %}</div>
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">
@ -133,14 +133,14 @@
<td class="text-center"> <td class="text-center">
{% if app.approved == None %} {% if app.approved == None %}
{% if app.reviewer_str %} {% if app.reviewer_str %}
<div class="badge bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div> <div class="badge text-bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
{% else %} {% else %}
<div class="badge bg-warning">{% translate "Pending" %}</div> <div class="badge text-bg-warning">{% translate "Pending" %}</div>
{% endif %} {% endif %}
{% elif app.approved == True %} {% elif app.approved == True %}
<div class="badge bg-success">{% translate "Approved" %}</div> <div class="badge text-bg-success">{% translate "Approved" %}</div>
{% else %} {% else %}
<div class="badge bg-danger">{% translate "Rejected" %}</div> <div class="badge text-bg-danger">{% translate "Rejected" %}</div>
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">
@ -177,14 +177,14 @@
<td class="text-center"> <td class="text-center">
{% if app.approved == None %} {% if app.approved == None %}
{% if app.reviewer_str %} {% if app.reviewer_str %}
<div class="badge bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div> <div class="badge text-bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
{% else %} {% else %}
<div class="badge bg-warning">{% translate "Pending" %}</div> <div class="badge text-bg-warning">{% translate "Pending" %}</div>
{% endif %} {% endif %}
{% elif app.approved == True %} {% elif app.approved == True %}
<div class="badge bg-success">{% translate "Approved" %}</div> <div class="badge text-bg-success">{% translate "Approved" %}</div>
{% else %} {% else %}
<div class="badge bg-danger">{% translate "Rejected" %}</div> <div class="badge text-bg-danger">{% translate "Rejected" %}</div>
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">

View File

@ -43,11 +43,11 @@
<td>{{ app.form.corp }}</td> <td>{{ app.form.corp }}</td>
<td class="text-center"> <td class="text-center">
{% if app.approved == None %} {% if app.approved == None %}
<div class="badge bg-warning">{% translate "Pending" %}</div> <div class="badge text-bg-warning">{% translate "Pending" %}</div>
{% elif app.approved == True %} {% elif app.approved == True %}
<div class="badge bg-success">{% translate "Approved" %}</div> <div class="badge text-bg-success">{% translate "Approved" %}</div>
{% else %} {% else %}
<div class="badge bg-danger">{% translate "Rejected" %}</div> <div class="badge text-bg-danger">{% translate "Rejected" %}</div>
{% endif %} {% endif %}
</td> </td>
<td class="text-end"> <td class="text-end">

View File

@ -31,7 +31,7 @@
</div> </div>
<div class="card mb-3"> <div class="card mb-3">
<div class="card-header bg-info"> <div class="card-header text-bg-info">
<div class="card-title mb-0">{% translate "Applicant" %}</div> <div class="card-title mb-0">{% translate "Applicant" %}</div>
</div> </div>
@ -50,7 +50,7 @@
</div> </div>
<div class="card mb-3"> <div class="card mb-3">
<div class="card-header bg-info"> <div class="card-header text-bg-info">
<div class="card-title mb-0">{% translate "Characters" %}</div> <div class="card-title mb-0">{% translate "Characters" %}</div>
</div> </div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-25 19:17+1000\n" "POT-Creation-Date: 2025-07-03 09:07+1000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,6 +18,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: allianceauth/analytics/apps.py:8
msgid "Analytics"
msgstr ""
#: allianceauth/analytics/models.py:22 #: allianceauth/analytics/models.py:22
msgid "Google Analytics Universal" msgid "Google Analytics Universal"
msgstr "" msgstr ""
@ -26,6 +30,10 @@ msgstr ""
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "" msgstr ""
#: allianceauth/authentication/apps.py:9
msgid "Authentication"
msgstr ""
#: allianceauth/authentication/constants.py:6 #: allianceauth/authentication/constants.py:6
msgid "" msgid ""
"This software has exceeded the error limit for ESI. If you are a user, " "This software has exceeded the error limit for ESI. If you are a user, "
@ -35,7 +43,7 @@ msgid ""
"esi on the EVE Online Discord. https://www.eveonline.com/discord" "esi on the EVE Online Discord. https://www.eveonline.com/discord"
msgstr "" msgstr ""
#: allianceauth/authentication/decorators.py:49 #: allianceauth/authentication/decorators.py:52
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "" msgstr ""
@ -48,91 +56,91 @@ msgstr ""
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:70 #: allianceauth/authentication/models.py:72
#: allianceauth/project_template/project_name/settings/base.py:105 #: allianceauth/project_template/project_name/settings/base.py:104
msgid "English" msgid "English"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:71 #: allianceauth/authentication/models.py:73
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:72 #: allianceauth/authentication/models.py:74
#: allianceauth/project_template/project_name/settings/base.py:107 #: allianceauth/project_template/project_name/settings/base.py:106
msgid "German" msgid "German"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:73 #: allianceauth/authentication/models.py:75
#: allianceauth/project_template/project_name/settings/base.py:108 #: allianceauth/project_template/project_name/settings/base.py:107
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:74 #: allianceauth/authentication/models.py:76
#: allianceauth/project_template/project_name/settings/base.py:109 #: allianceauth/project_template/project_name/settings/base.py:108
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:75 #: allianceauth/authentication/models.py:77
#: allianceauth/project_template/project_name/settings/base.py:110 #: allianceauth/project_template/project_name/settings/base.py:109
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:76 #: allianceauth/authentication/models.py:78
#: allianceauth/project_template/project_name/settings/base.py:111 #: allianceauth/project_template/project_name/settings/base.py:110
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:77
#: allianceauth/project_template/project_name/settings/base.py:112
msgid "French"
msgstr ""
#: allianceauth/authentication/models.py:78
#: allianceauth/project_template/project_name/settings/base.py:115
msgid "Russian"
msgstr ""
#: allianceauth/authentication/models.py:79 #: allianceauth/authentication/models.py:79
#: allianceauth/project_template/project_name/settings/base.py:113 #: allianceauth/project_template/project_name/settings/base.py:111
msgid "Dutch" msgid "French"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:80
#: allianceauth/project_template/project_name/settings/base.py:114 #: allianceauth/project_template/project_name/settings/base.py:114
msgid "Polish" msgid "Russian"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:81
#: allianceauth/project_template/project_name/settings/base.py:116 #: allianceauth/project_template/project_name/settings/base.py:112
msgid "Ukrainian" msgid "Dutch"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:82
#: allianceauth/project_template/project_name/settings/base.py:117 #: allianceauth/project_template/project_name/settings/base.py:113
msgid "Polish"
msgstr ""
#: allianceauth/authentication/models.py:83
#: allianceauth/project_template/project_name/settings/base.py:115
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:84
#: allianceauth/project_template/project_name/settings/base.py:116
msgid "Simplified Chinese" msgid "Simplified Chinese"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:98 #: allianceauth/authentication/models.py:100
#: allianceauth/menu/templates/menu/menu-user.html:42 #: allianceauth/menu/templates/menu/menu-user.html:67
msgid "Language" msgid "Language"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:103 #: allianceauth/authentication/models.py:105
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:107 #: allianceauth/authentication/models.py:109
#: allianceauth/menu/templates/menu/menu-user.html:46 #: allianceauth/theme/templates/theme/theme_select.html:4
msgid "Theme" msgid "Theme"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:124 #: allianceauth/authentication/models.py:126
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:125 #: allianceauth/authentication/models.py:127
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "" msgstr ""
@ -194,6 +202,8 @@ msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:7 #: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:11 #: allianceauth/authentication/templates/authentication/tokens.html:11
#: allianceauth/menu/templates/menu/menu-user.html:133
#: allianceauth/menu/templates/menu/menu-user.html:136
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management" msgid "Token Management"
msgstr "" msgstr ""
@ -267,53 +277,53 @@ msgstr ""
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:157 #: allianceauth/authentication/views.py:158
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
"account." "account."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:164 #: allianceauth/authentication/views.py:165
#, python-format #, python-format
msgid "Changed main character to %s" msgid "Changed main character to %s"
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:178 #: allianceauth/authentication/views.py:179
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:180 #: allianceauth/authentication/views.py:181
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:225 #: allianceauth/authentication/views.py:226
msgid "" msgid ""
"Unable to authenticate as the selected character. Please log in with the " "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account." "main character associated with this account."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:292 #: allianceauth/authentication/views.py:293
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:353 #: allianceauth/authentication/views.py:354
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:359 #: allianceauth/authentication/views.py:360
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "" msgstr ""
#: allianceauth/authentication/views.py:365 #: allianceauth/authentication/views.py:366
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "" msgstr ""
#: allianceauth/corputils/auth_hooks.py:12 #: allianceauth/corputils/apps.py:8 allianceauth/corputils/auth_hooks.py:12
msgid "Corporation Stats" msgid "Corporation Stats"
msgstr "" msgstr ""
@ -419,6 +429,10 @@ msgstr ""
msgid "Failed to gather corporation statistics with selected token." msgid "Failed to gather corporation statistics with selected token."
msgstr "" msgstr ""
#: allianceauth/crontab/apps.py:16
msgid "Crontab"
msgstr ""
#: allianceauth/crontab/models.py:13 #: allianceauth/crontab/models.py:13
msgid "Minute Offset" msgid "Minute Offset"
msgstr "" msgstr ""
@ -452,6 +466,15 @@ msgstr ""
msgid "This CSS will be added to the site after the default CSS." msgid "This CSS will be added to the site after the default CSS."
msgstr "" msgstr ""
#: allianceauth/eveonline/apps.py:8
msgid "EVE Online"
msgstr ""
#: allianceauth/eveonline/autogroups/apps.py:8
msgid "EVE Online Autogroups"
msgstr ""
#: allianceauth/fleetactivitytracking/apps.py:8
#: allianceauth/fleetactivitytracking/auth_hooks.py:10 #: allianceauth/fleetactivitytracking/auth_hooks.py:10
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/characternotexisting.html:10 #: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/characternotexisting.html:10
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkcreate.html:11 #: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkcreate.html:11
@ -561,9 +584,11 @@ msgstr ""
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:75 #: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:75
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:44 #: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:44
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:92 #: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:92
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:17
#: allianceauth/templates/allianceauth/top-menu.html:23 #: allianceauth/templates/allianceauth/top-menu.html:23
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:18
#: allianceauth/timerboard/templates/timerboard/timertable.html:12 #: allianceauth/timerboard/templates/timerboard/timertable.html:12
msgid "Eve Time" msgid "EVE time"
msgstr "" msgstr ""
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:49 #: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:49
@ -728,6 +753,11 @@ msgid ""
"character needs to be online." "character needs to be online."
msgstr "" msgstr ""
#: allianceauth/framework/apps.py:16
msgid "Framework"
msgstr ""
#: allianceauth/groupmanagement/apps.py:8
#: allianceauth/groupmanagement/auth_hooks.py:18 #: allianceauth/groupmanagement/auth_hooks.py:18
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:18 #: allianceauth/groupmanagement/templates/groupmanagement/groups.html:18
msgid "Group Management" msgid "Group Management"
@ -956,7 +986,7 @@ msgid "Hidden"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:45 #: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:45
#: allianceauth/templates/allianceauth/admin-status/overview.html:16 #: allianceauth/templates/allianceauth/admin-status/overview.html:15
msgid "Open" msgid "Open"
msgstr "" msgstr ""
@ -1009,7 +1039,7 @@ msgstr ""
#: allianceauth/hrapplications/templates/hrapplications/management.html:182 #: allianceauth/hrapplications/templates/hrapplications/management.html:182
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:46 #: allianceauth/hrapplications/templates/hrapplications/searchview.html:46
#: allianceauth/hrapplications/templates/hrapplications/view.html:25 #: allianceauth/hrapplications/templates/hrapplications/view.html:25
#: allianceauth/srp/templates/srp/data.html:117 #: allianceauth/srp/templates/srp/data.html:120
#: allianceauth/srp/templates/srp/management.html:87 #: allianceauth/srp/templates/srp/management.html:87
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
@ -1151,6 +1181,10 @@ msgstr ""
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "" msgstr ""
#: allianceauth/hrapplications/apps.py:8
msgid "HR Applications"
msgstr ""
#: allianceauth/hrapplications/auth_hooks.py:15 #: allianceauth/hrapplications/auth_hooks.py:15
msgid "Applications" msgid "Applications"
msgstr "" msgstr ""
@ -1222,7 +1256,7 @@ msgstr ""
#: allianceauth/hrapplications/templates/hrapplications/management.html:185 #: allianceauth/hrapplications/templates/hrapplications/management.html:185
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:48 #: allianceauth/hrapplications/templates/hrapplications/searchview.html:48
#: allianceauth/hrapplications/templates/hrapplications/view.html:21 #: allianceauth/hrapplications/templates/hrapplications/view.html:21
#: allianceauth/srp/templates/srp/data.html:109 #: allianceauth/srp/templates/srp/data.html:112
msgid "Approved" msgid "Approved"
msgstr "" msgstr ""
@ -1230,7 +1264,7 @@ msgstr ""
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
#: allianceauth/hrapplications/templates/hrapplications/management.html:187 #: allianceauth/hrapplications/templates/hrapplications/management.html:187
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:50 #: allianceauth/hrapplications/templates/hrapplications/searchview.html:50
#: allianceauth/srp/templates/srp/data.html:113 #: allianceauth/srp/templates/srp/data.html:116
msgid "Rejected" msgid "Rejected"
msgstr "" msgstr ""
@ -1345,7 +1379,7 @@ msgstr ""
msgid "children" msgid "children"
msgstr "" msgstr ""
#: allianceauth/menu/admin.py:90 allianceauth/menu/models.py:21 #: allianceauth/menu/admin.py:90 allianceauth/menu/models.py:22
msgid "text" msgid "text"
msgstr "" msgstr ""
@ -1357,11 +1391,15 @@ msgstr ""
msgid "visible" msgid "visible"
msgstr "" msgstr ""
#: allianceauth/menu/apps.py:16
msgid "Menu"
msgstr ""
#: allianceauth/menu/constants.py:16 #: allianceauth/menu/constants.py:16
msgid "app" msgid "app"
msgstr "" msgstr ""
#: allianceauth/menu/constants.py:17 allianceauth/menu/models.py:37 #: allianceauth/menu/constants.py:17 allianceauth/menu/models.py:38
msgid "folder" msgid "folder"
msgstr "" msgstr ""
@ -1373,47 +1411,47 @@ msgstr ""
msgid "type" msgid "type"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:22 #: allianceauth/menu/models.py:23
msgid "Text to show on menu" msgid "Text to show on menu"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:27 #: allianceauth/menu/models.py:28
msgid "order" msgid "order"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:28 #: allianceauth/menu/models.py:29
msgid "Order of the menu. Lowest First" msgid "Order of the menu. Lowest First"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:38 #: allianceauth/menu/models.py:39
msgid "Folder this item is in (optional)" msgid "Folder this item is in (optional)"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:42 #: allianceauth/menu/models.py:43
msgid "is hidden" msgid "is hidden"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:44 #: allianceauth/menu/models.py:45
msgid "" msgid ""
"Hide this menu item.If this item is a folder all items under it will be " "Hide this menu item.If this item is a folder all items under it will be "
"hidden too" "hidden too"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:59 #: allianceauth/menu/models.py:60
msgid "icon classes" msgid "icon classes"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:61 #: allianceauth/menu/models.py:62
msgid "" msgid ""
"Font Awesome classes to show as icon on menu, e.g. <code>fa-solid fa-house</" "Font Awesome classes to show as icon on menu, e.g. <code>fa-solid fa-house</"
"code>" "code>"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:67 #: allianceauth/menu/models.py:68
msgid "url" msgid "url"
msgstr "" msgstr ""
#: allianceauth/menu/models.py:68 #: allianceauth/menu/models.py:69
msgid "External URL this menu items will link to" msgid "External URL this menu items will link to"
msgstr "" msgstr ""
@ -1422,44 +1460,63 @@ msgid "Add folder"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-notification-block.html:12 #: allianceauth/menu/templates/menu/menu-notification-block.html:12
#: allianceauth/notifications/apps.py:8
#: allianceauth/notifications/templates/notifications/list.html:7 #: allianceauth/notifications/templates/notifications/list.html:7
#: allianceauth/notifications/templates/notifications/list.html:11 #: allianceauth/notifications/templates/notifications/list.html:11
#: allianceauth/templates/allianceauth/notifications_menu_item.html:6 #: allianceauth/templates/allianceauth/notifications_menu_item.html:6
msgid "Notifications" msgid "Notifications"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:56 #: allianceauth/menu/templates/menu/menu-user.html:77
msgid "Super User" msgid "Super User"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:70 #: allianceauth/menu/templates/menu/menu-user.html:83
#: allianceauth/menu/templates/menu/menu-user.html:86
msgid "Alliance Auth Documentation"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:94
#: allianceauth/menu/templates/menu/menu-user.html:97
msgid "Alliance Auth Discord"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:105
#: allianceauth/menu/templates/menu/menu-user.html:108
msgid "Alliance Auth Git"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:118
#: allianceauth/menu/templates/menu/menu-user.html:121
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:82 #: allianceauth/menu/templates/menu/menu-user.html:144
#: allianceauth/menu/templates/menu/menu-user.html:147
msgid "Sign Out" msgid "Sign Out"
msgstr "" msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:86 #: allianceauth/menu/templates/menu/menu-user.html:155
#: allianceauth/menu/templates/menu/menu-user.html:158
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17 #: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18 #: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In" msgid "Sign In"
msgstr "" msgstr ""
#: allianceauth/notifications/models.py:21 #: allianceauth/notifications/models.py:22
msgid "danger" msgid "danger"
msgstr "" msgstr ""
#: allianceauth/notifications/models.py:22 #: allianceauth/notifications/models.py:23
msgid "warning" msgid "warning"
msgstr "" msgstr ""
#: allianceauth/notifications/models.py:23 #: allianceauth/notifications/models.py:24
msgid "info" msgid "info"
msgstr "" msgstr ""
#: allianceauth/notifications/models.py:24 #: allianceauth/notifications/models.py:25
msgid "success" msgid "success"
msgstr "" msgstr ""
@ -1516,7 +1573,7 @@ msgstr ""
msgid "Deleted all read notifications." msgid "Deleted all read notifications."
msgstr "" msgstr ""
#: allianceauth/optimer/auth_hooks.py:12 #: allianceauth/optimer/apps.py:8 allianceauth/optimer/auth_hooks.py:12
msgid "Fleet Operations" msgid "Fleet Operations"
msgstr "" msgstr ""
@ -1590,11 +1647,6 @@ msgstr ""
msgid "Form Up System" msgid "Form Up System"
msgstr "" msgstr ""
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:17
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:18
msgid "EVE Time"
msgstr ""
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14 #: allianceauth/optimer/templates/optimer/fleetoptable.html:14
#: allianceauth/timerboard/templates/timerboard/timertable.html:13 #: allianceauth/timerboard/templates/timerboard/timertable.html:13
msgid "Local Time" msgid "Local Time"
@ -1610,7 +1662,7 @@ msgstr ""
#: allianceauth/optimer/templates/optimer/management.html:28 #: allianceauth/optimer/templates/optimer/management.html:28
#: allianceauth/timerboard/templates/timerboard/view.html:32 #: allianceauth/timerboard/templates/timerboard/view.html:32
msgid "Current Eve Time:" msgid "Current EVE time:"
msgstr "" msgstr ""
#: allianceauth/optimer/templates/optimer/management.html:36 #: allianceauth/optimer/templates/optimer/management.html:36
@ -1655,6 +1707,7 @@ msgstr ""
msgid "Saved changes to operation timer for %(opname)s." msgid "Saved changes to operation timer for %(opname)s."
msgstr "" msgstr ""
#: allianceauth/permissions_tool/apps.py:8
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:7 #: allianceauth/permissions_tool/templates/permissions_tool/audit.html:7
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:11 #: allianceauth/permissions_tool/templates/permissions_tool/audit.html:11
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:17 #: allianceauth/permissions_tool/templates/permissions_tool/audit.html:17
@ -1711,7 +1764,7 @@ msgstr ""
msgid "Successfully set your {self.service_name} password" msgid "Successfully set your {self.service_name} password"
msgstr "" msgstr ""
#: allianceauth/services/auth_hooks.py:12 #: allianceauth/services/apps.py:8 allianceauth/services/auth_hooks.py:12
msgid "Services" msgid "Services"
msgstr "" msgstr ""
@ -1775,6 +1828,10 @@ msgstr ""
msgid "Password must be at least 8 characters long." msgid "Password must be at least 8 characters long."
msgstr "" msgstr ""
#: allianceauth/services/modules/discord/apps.py:8
msgid "Discord Service"
msgstr ""
#: allianceauth/services/modules/discord/models.py:187 #: allianceauth/services/modules/discord/models.py:187
msgid "Discord Account Disabled" msgid "Discord Account Disabled"
msgstr "" msgstr ""
@ -1823,6 +1880,10 @@ msgid ""
"again." "again."
msgstr "" msgstr ""
#: allianceauth/services/modules/discourse/apps.py:8
msgid "Discourse Service"
msgstr ""
#: allianceauth/services/modules/discourse/templates/services/discourse/discourse_service_ctrl.html:5 #: allianceauth/services/modules/discourse/templates/services/discourse/discourse_service_ctrl.html:5
msgid "Discourse" msgid "Discourse"
msgstr "" msgstr ""
@ -1853,6 +1914,14 @@ msgstr ""
msgid "Invalid payload. Please contact support if this problem persists." msgid "Invalid payload. Please contact support if this problem persists."
msgstr "" msgstr ""
#: allianceauth/services/modules/example/apps.py:8
msgid "Example Service"
msgstr ""
#: allianceauth/services/modules/ips4/apps.py:8
msgid "IPS4 Service"
msgstr ""
#: allianceauth/services/modules/ips4/views.py:31 #: allianceauth/services/modules/ips4/views.py:31
msgid "Activated IPSuite4 account." msgid "Activated IPSuite4 account."
msgstr "" msgstr ""
@ -1876,6 +1945,10 @@ msgstr ""
msgid "Deactivated IPSuite4 account." msgid "Deactivated IPSuite4 account."
msgstr "" msgstr ""
#: allianceauth/services/modules/mumble/apps.py:8
msgid "Mumble Service"
msgstr ""
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_connection_history.html:7 #: allianceauth/services/modules/mumble/templates/services/mumble/mumble_connection_history.html:7
msgid "Mumble" msgid "Mumble"
msgstr "" msgstr ""
@ -1931,6 +2004,10 @@ msgstr ""
msgid "Mumble Connection History" msgid "Mumble Connection History"
msgstr "" msgstr ""
#: allianceauth/services/modules/openfire/apps.py:8
msgid "Openfire Service"
msgstr ""
#: allianceauth/services/modules/openfire/auth_hooks.py:27 #: allianceauth/services/modules/openfire/auth_hooks.py:27
msgid "Jabber" msgid "Jabber"
msgstr "" msgstr ""
@ -1982,6 +2059,10 @@ msgstr ""
msgid "Set jabber password." msgid "Set jabber password."
msgstr "" msgstr ""
#: allianceauth/services/modules/phpbb3/apps.py:8
msgid "phpBB3 Service"
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:34 #: allianceauth/services/modules/phpbb3/views.py:34
msgid "Activated forum account." msgid "Activated forum account."
msgstr "" msgstr ""
@ -2005,6 +2086,10 @@ msgstr ""
msgid "Set forum password." msgid "Set forum password."
msgstr "" msgstr ""
#: allianceauth/services/modules/smf/apps.py:8
msgid "SMF Service"
msgstr ""
#: allianceauth/services/modules/smf/views.py:52 #: allianceauth/services/modules/smf/views.py:52
msgid "Activated SMF account." msgid "Activated SMF account."
msgstr "" msgstr ""
@ -2028,6 +2113,10 @@ msgstr ""
msgid "Set SMF password." msgid "Set SMF password."
msgstr "" msgstr ""
#: allianceauth/services/modules/teamspeak3/apps.py:8
msgid "TeamSpeak 3 Service"
msgstr ""
#: allianceauth/services/modules/teamspeak3/forms.py:14 #: allianceauth/services/modules/teamspeak3/forms.py:14
#, python-format #, python-format
msgid "Unable to locate user %s on server" msgid "Unable to locate user %s on server"
@ -2077,6 +2166,10 @@ msgstr ""
msgid "Reset TeamSpeak3 permission key." msgid "Reset TeamSpeak3 permission key."
msgstr "" msgstr ""
#: allianceauth/services/modules/xenforo/apps.py:8
msgid "Xenforo Service"
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:30 #: allianceauth/services/modules/xenforo/views.py:30
msgid "Activated XenForo account." msgid "Activated XenForo account."
msgstr "" msgstr ""
@ -2186,7 +2279,7 @@ msgid ""
"Some services provide different options. Hover over the buttons to see more." "Some services provide different options. Hover over the buttons to see more."
msgstr "" msgstr ""
#: allianceauth/srp/auth_hooks.py:14 #: allianceauth/srp/apps.py:8 allianceauth/srp/auth_hooks.py:14
msgid "Ship Replacement" msgid "Ship Replacement"
msgstr "" msgstr ""
@ -2261,18 +2354,18 @@ msgid "Mark Completed"
msgstr "" msgstr ""
#: allianceauth/srp/templates/srp/data.html:48 #: allianceauth/srp/templates/srp/data.html:48
#: allianceauth/srp/templates/srp/data.html:139 #: allianceauth/srp/templates/srp/data.html:142
msgid "Total Losses:" msgid "Total Losses:"
msgstr "" msgstr ""
#: allianceauth/srp/templates/srp/data.html:49 #: allianceauth/srp/templates/srp/data.html:49
#: allianceauth/srp/templates/srp/data.html:140 #: allianceauth/srp/templates/srp/data.html:143
#: allianceauth/srp/templates/srp/management.html:36 #: allianceauth/srp/templates/srp/management.html:36
msgid "Total ISK Cost:" msgid "Total ISK Cost:"
msgstr "" msgstr ""
#: allianceauth/srp/templates/srp/data.html:60 #: allianceauth/srp/templates/srp/data.html:60
#: allianceauth/srp/templates/srp/data.html:151 #: allianceauth/srp/templates/srp/data.html:154
msgid "Are you sure you want to delete SRP requests?" msgid "Are you sure you want to delete SRP requests?"
msgstr "" msgstr ""
@ -2304,12 +2397,12 @@ msgstr ""
msgid "Post Time" msgid "Post Time"
msgstr "" msgstr ""
#: allianceauth/srp/templates/srp/data.html:99 #: allianceauth/srp/templates/srp/data.html:102
#: allianceauth/srp/templates/srp/management.html:70 #: allianceauth/srp/templates/srp/management.html:70
msgid "Link" msgid "Link"
msgstr "" msgstr ""
#: allianceauth/srp/templates/srp/data.html:160 #: allianceauth/srp/templates/srp/data.html:163
msgid "No SRP requests for this fleet." msgid "No SRP requests for this fleet."
msgstr "" msgstr ""
@ -2493,52 +2586,48 @@ msgstr ""
msgid "Alliance Auth Notifications" msgid "Alliance Auth Notifications"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:18 #: allianceauth/templates/allianceauth/admin-status/overview.html:20
msgid "Closed"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:24
msgid "No notifications at this time" msgid "No notifications at this time"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:33 #: allianceauth/templates/allianceauth/admin-status/overview.html:29
msgid "Powered by GitLab" msgid "Powered by GitLab"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:39 #: allianceauth/templates/allianceauth/admin-status/overview.html:35
msgid "Support Discord" msgid "Support Discord"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:49
#: allianceauth/templates/allianceauth/admin-status/overview.html:53 #: allianceauth/templates/allianceauth/admin-status/overview.html:53
#: allianceauth/templates/allianceauth/admin-status/overview.html:57
msgid "Software Version" msgid "Software Version"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:60 #: allianceauth/templates/allianceauth/admin-status/overview.html:56
msgid "Current" msgid "Current"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:67 #: allianceauth/templates/allianceauth/admin-status/overview.html:63
msgid "Latest Stable" msgid "Latest Stable"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:72 #: allianceauth/templates/allianceauth/admin-status/overview.html:68
msgid "Update available" msgid "Update available"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:80 #: allianceauth/templates/allianceauth/admin-status/overview.html:76
msgid "Latest Pre-Release" msgid "Latest Pre-Release"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:85 #: allianceauth/templates/allianceauth/admin-status/overview.html:81
msgid "Pre-Release available" msgid "Pre-Release available"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:91
msgid "Task Queue" msgid "Task Queue"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:100 #: allianceauth/templates/allianceauth/admin-status/overview.html:96
#, python-format #, python-format
msgid "" msgid ""
"\n" "\n"
@ -2547,11 +2636,11 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:116 #: allianceauth/templates/allianceauth/admin-status/overview.html:112
msgid "running" msgid "running"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:117 #: allianceauth/templates/allianceauth/admin-status/overview.html:113
msgid "queued" msgid "queued"
msgstr "" msgstr ""
@ -2576,10 +2665,17 @@ msgstr ""
msgid "Toggle navigation" msgid "Toggle navigation"
msgstr "" msgstr ""
#: allianceauth/theme/templates/theme/theme_select.html:7 #: allianceauth/theme/templates/theme/theme_select.html:11
msgid "Select Theme" msgid "Select Theme"
msgstr "" msgstr ""
#: allianceauth/timerboard/apps.py:8
#: allianceauth/timerboard/templates/timerboard/form.html:10
#: allianceauth/timerboard/templates/timerboard/timer_confirm_delete.html:10
#: allianceauth/timerboard/templates/timerboard/view.html:14
msgid "Structure Timers"
msgstr ""
#: allianceauth/timerboard/form.py:36 #: allianceauth/timerboard/form.py:36
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:14 #: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:14
#: allianceauth/timerboard/templates/timerboard/timertable.html:8 #: allianceauth/timerboard/templates/timerboard/timertable.html:8
@ -2766,6 +2862,10 @@ msgstr ""
msgid "Abandoned" msgid "Abandoned"
msgstr "" msgstr ""
#: allianceauth/timerboard/models.py:60
msgid "Theft"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:7 #: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:7
#: allianceauth/timerboard/templates/timerboard/view.html:54 #: allianceauth/timerboard/templates/timerboard/view.html:54
msgid "Upcoming Timers" msgid "Upcoming Timers"
@ -2775,12 +2875,6 @@ msgstr ""
msgid "Timer" msgid "Timer"
msgstr "" msgstr ""
#: allianceauth/timerboard/templates/timerboard/form.html:10
#: allianceauth/timerboard/templates/timerboard/timer_confirm_delete.html:10
#: allianceauth/timerboard/templates/timerboard/view.html:14
msgid "Structure Timers"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/form.html:25 #: allianceauth/timerboard/templates/timerboard/form.html:25
msgid "Structure Timer Details" msgid "Structure Timer Details"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
import logging import logging
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -12,6 +13,7 @@ logger = logging.getLogger(__name__)
class MenuConfig(AppConfig): class MenuConfig(AppConfig):
name = "allianceauth.menu" name = "allianceauth.menu"
label = "menu" label = "menu"
verbose_name = _("Menu")
def ready(self): def ready(self):
from allianceauth.menu.core import smart_sync from allianceauth.menu.core import smart_sync

View File

@ -1,3 +1,4 @@
from typing import ClassVar
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 _
@ -68,7 +69,7 @@ class MenuItem(models.Model):
help_text=_("External URL this menu items will link to"), help_text=_("External URL this menu items will link to"),
) )
objects = MenuItemManager() objects: ClassVar[MenuItemManager] = MenuItemManager()
def __str__(self) -> str: def __str__(self) -> str:
return self.text return self.text

View File

@ -27,7 +27,7 @@
</a> </a>
{% if item.count >= 1 %} {% if item.count >= 1 %}
<span class="badge bg-primary m-2 align-self-center{% if not item.is_folder %} me-2{% endif %}"> <span class="badge text-bg-primary m-2 align-self-center{% if not item.is_folder %} me-2{% endif %}">
{{ item.count }} {{ item.count }}
</span> </span>
{% elif item.url %} {% elif item.url %}

View File

@ -2,8 +2,8 @@
{% load navactive %} {% load navactive %}
{% load auth_notifications %} {% load auth_notifications %}
<li class="nav-item {% navactive request 'notifications:' %}" id="menu_item_notifications"> <li class="nav-item" id="menu_item_notifications">
<a class="nav-link" href="{% url 'notifications:list' %}"> <a class="nav-link {% navactive request 'notifications:' %}" href="{% url 'notifications:list' %}">
{% with unread_count=request.user|user_unread_notification_count %} {% with unread_count=request.user|user_unread_notification_count %}
<i class="fa-solid fa-bell{% if unread_count %} text-danger{% endif %}"></i> <i class="fa-solid fa-bell{% if unread_count %} text-danger{% endif %}"></i>
{% endwith %} {% endwith %}

View File

@ -2,28 +2,51 @@
{% load evelinks %} {% load evelinks %}
{% load theme_tags %} {% load theme_tags %}
<div id="aa-user-info" class="w-100 d-flex flex-column justify-content-center align-items-center text-center py-1 border-top border-secondary {% if not user.is_authenticated %}position-absolute bottom-0{% endif %}"> <div
id="aa-user-info"
class="w-100 d-flex flex-column justify-content-center align-items-center text-center py-1 border-top border-secondary {% if not user.is_authenticated %}position-absolute bottom-0{% endif %}"
>
<div class="d-flex mb-0 w-100"> <div class="d-flex mb-0 w-100">
<div class="p-2 position-relative m-2"> <div class="p-2 position-relative m-2">
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% with request.user.profile.main_character as main %} {% with request.user.profile.main_character as main %}
<img class="rounded-circle" src="{{ main.character_id|character_portrait_url:64 }}" alt="{{ main.character_name }}"> <img
<img class="rounded-circle position-absolute bottom-0 start-0" src="{{ main.corporation_logo_url_32 }}" alt="{{ main.corporation_name }}"> class="rounded-circle"
src="{{ main.character_id|character_portrait_url:64 }}"
alt="{{ main.character_name }}"
>
<img
class="rounded-circle position-absolute bottom-0 start-0"
src="{{ main.corporation_logo_url_32 }}"
alt="{{ main.corporation_name }}"
>
{% if main.alliance_id %} {% if main.alliance_id %}
<img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.alliance_logo_url_32 }}" alt="{{ main.alliance_name }}"> <img
class="rounded-circle position-absolute bottom-0 end-0"
src="{{ main.alliance_logo_url_32 }}"
alt="{{ main.alliance_name }}"
>
{% elif main.faction_id %} {% elif main.faction_id %}
<img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.faction_logo_url_32 }}" alt="{{ main.faction_name }}"> <img
class="rounded-circle position-absolute bottom-0 end-0"
src="{{ main.faction_logo_url_32 }}"
alt="{{ main.faction_name }}"
>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% include "bundles/image-auth-logo.html" with logo_width="64px" %} {% include "bundles/image-auth-logo.html" with logo_width="64px" %}
{% endif %} {% endif %}
</div> </div>
<div class="align-self-center text-start"> <div class="align-self-center text-start">
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% with request.user.profile.main_character as main %} {% with request.user.profile.main_character as main %}
<h5 class="m-0">{{ main.character_name }}</h5> <h5 class="m-0">{{ main.character_name }}</h5>
<p class="m-0 small">{{ main.corporation_name }}</p> <p class="m-0 small">{{ main.corporation_name }}</p>
{% if main.alliance_id %} {% if main.alliance_id %}
<p class="m-0 small">{{ main.alliance_name }}</p> <p class="m-0 small">{{ main.alliance_name }}</p>
{% elif main.faction_id %} {% elif main.faction_id %}
@ -34,56 +57,106 @@
<h5 class="m-0">{{ SITE_NAME }}</h5> <h5 class="m-0">{{ SITE_NAME }}</h5>
{% endif %} {% endif %}
</div> </div>
<div class="ms-auto dropup"> <div class="ms-auto dropup">
<button type="button" class="h-100 btn" data-bs-toggle="dropdown" aria-expanded="false"> <button type="button" class="h-100 btn" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-solid fa-gear fa-fw text-light"></i> <i class="fa-solid fa-gear fa-fw text-light"></i>
</button> </button>
<ul class="dropdown-menu" style="min-width: 200px;"> <ul class="dropdown-menu" style="min-width: 200px;">
<li><h6 class="dropdown-header">{% translate "Language" %}</h6></li> <li><h6 class="dropdown-header">{% translate "Language" %}</h6></li>
<li>
<a class="dropdown-item">{% include "public/lang_select.html" %}</a>
</li>
<li><h6 class="dropdown-header">{% translate "Theme" %}</h6></li>
<li> <li>
<a class="dropdown-item"> {% include "public/lang_select.html" %}
{% theme_select %}
</a>
</li> </li>
{% theme_select %}
{% if user.is_superuser %} {% if user.is_superuser %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><h6 class="dropdown-header">{% translate "Super User" %}</h6></li> <li><h6 class="dropdown-header">{% translate "Super User" %}</h6></li>
<li> <li>
<a class="dropdown-item" href="https://allianceauth.readthedocs.io/" title="Alliance Auth Documentation"><i class="fa-solid fa-book fa-fw"></i> Alliance Auth Documentation</a> <a
class="dropdown-item"
href="https://allianceauth.readthedocs.io/"
title="{% translate 'Alliance Auth Documentation' %}"
>
<i class="fa-solid fa-book fa-fw"></i>
{% translate "Alliance Auth Documentation" %}
</a>
</li> </li>
<li> <li>
<a class="dropdown-item" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i> Alliance Auth Discord</a> <a
class="dropdown-item"
href="https://discord.gg/fjnHAmk"
title="{% translate 'Alliance Auth Discord' %}"
>
<i class="fa-brands fa-discord fa-fw"></i>
{% translate "Alliance Auth Discord" %}
</a>
</li> </li>
<li> <li>
<a class="dropdown-item" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i> Alliance Auth Git</a> <a
class="dropdown-item"
href="https://gitlab.com/allianceauth/allianceauth"
title="{% translate 'Alliance Auth Git' %}"
>
<i class="fa-brands fa-gitlab fa-fw"></i>
{% translate "Alliance Auth Git" %}
</a>
</li> </li>
{% endif %} {% endif %}
{% if user.is_staff %} {% if user.is_staff %}
<li> <li>
<a class="dropdown-item" href="{% url 'admin:index' %}"> <a
<i class="fa-solid fa-gear fa-fw"></i> {% translate "Admin" %} class="dropdown-item"
href="{% url 'admin:index' %}"
title="{% translate 'Admin' %}"
>
<i class="fa-solid fa-gear fa-fw"></i>
{% translate "Admin" %}
</a> </a>
</li> </li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li> <li>
<a class="dropdown-item" href="{% url 'authentication:token_management' %}"> <a
<i class="fa-solid fa-user-lock fa-fw"></i> Token Management class="dropdown-item"
href="{% url 'authentication:token_management' %}"
title="{% translate 'Token Management' %}"
>
<i class="fa-solid fa-user-lock fa-fw"></i>
{% translate "Token Management" %}
</a> </a>
</li> </li>
<li> <li>
<a class="dropdown-item text-danger" href="{% url 'logout' %}" title="{% translate 'Sign Out' %}"><i class="fa-solid fa-right-from-bracket fa-fw "></i> {% translate 'Sign Out' %}</a> <a
class="dropdown-item text-danger"
href="{% url 'logout' %}"
title="{% translate 'Sign Out' %}"
>
<i class="fa-solid fa-right-from-bracket fa-fw"></i>
{% translate 'Sign Out' %}
</a>
</li> </li>
{% else %} {% else %}
<li> <li>
<a class="dropdown-item text-success" href="{% url 'authentication:login' %}" title="{% translate 'Sign In' %}"> <i class="fa-solid fa-right-to-bracket fa-fw "></i> {% translate 'Sign In' %}</a> <a
class="dropdown-item text-success"
href="{% url 'authentication:login' %}"
title="{% translate 'Sign In' %}"
>
<i class="fa-solid fa-right-to-bracket fa-fw"></i>
{% translate 'Sign In' %}
</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -5,7 +5,7 @@
<div class="col-auto px-0"> <div class="col-auto px-0">
<div class="collapse collapse-horizontal" tabindex="-1" id="sidebar"> <div class="collapse collapse-horizontal" tabindex="-1" id="sidebar">
<div> <div>
<div class="nav-padding navbar-dark bg-dark text-light px-0 d-flex flex-column overflow-hidden vh-100 {% if not user.is_authenticated %}position-relative{% endif %}"> <div class="nav-padding navbar-dark text-bg-dark px-0 d-flex flex-column overflow-hidden vh-100 {% if not user.is_authenticated %}position-relative{% endif %}">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<ul id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2"> <ul id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2">
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0"> <li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">

View File

@ -186,7 +186,7 @@ class TestRenderDefaultMenu(TestCase):
classes = "fa-solid fa-users-gear" classes = "fa-solid fa-users-gear"
url_name = "groupmanagement:management" url_name = "groupmanagement:management"
def render(self, request): def render(Self, request):
# simulate no perms # simulate no perms
return "" return ""

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 NotificationsConfig(AppConfig): class NotificationsConfig(AppConfig):
name = 'allianceauth.notifications' name = 'allianceauth.notifications'
label = 'notifications' label = 'notifications'
verbose_name = _('Notifications')

View File

@ -1,4 +1,5 @@
import logging import logging
from typing import ClassVar
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -56,7 +57,7 @@ class Notification(models.Model):
timestamp = models.DateTimeField(auto_now_add=True, db_index=True) timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
viewed = models.BooleanField(default=False, db_index=True) viewed = models.BooleanField(default=False, db_index=True)
objects = NotificationManager() objects: ClassVar[NotificationManager] = NotificationManager()
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.user}: {self.title}" return f"{self.user}: {self.title}"

View File

@ -15,14 +15,14 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" id="unread-tab" data-bs-toggle="tab" data-bs-target="#unread" type="button" role="tab" aria-controls="unread" aria-selected="true"> <a class="nav-link active" id="unread-tab" data-bs-toggle="tab" data-bs-target="#unread" type="button" role="tab" aria-controls="unread" aria-selected="true">
{% translate "Unread" %} {% translate "Unread" %}
<span class="badge bg-secondary">{{ unread|length }}</span> <span class="badge text-bg-secondary">{{ unread|length }}</span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="read-tab" data-bs-toggle="tab" data-bs-target="#read" type="button" role="tab" aria-controls="read" aria-selected="false"> <a class="nav-link" id="read-tab" data-bs-toggle="tab" data-bs-target="#read" type="button" role="tab" aria-controls="read" aria-selected="false">
{% translate "Read" %} {% translate "Read" %}
<span class="badge bg-secondary">{{ read|length }}</span> <span class="badge text-bg-secondary">{{ read|length }}</span>
</a> </a>
</li> </li>
{% endblock %} {% endblock %}

View File

@ -17,7 +17,7 @@
</div> </div>
<div class="card border-{{ notif.level }}"> <div class="card border-{{ notif.level }}">
<div class="card-header bg-{{ notif.level }}"> <div class="card-header text-bg-{{ notif.level }}">
{{ notif.timestamp }} {{ notif.timestamp }}
{{ notif.title }} {{ notif.title }}
</div> </div>

View File

@ -1,4 +1,4 @@
from unittest.mock import patch from unittest.mock import patch, Mock
from django.test import TestCase, override_settings from django.test import TestCase, override_settings

View File

@ -1,6 +1,6 @@
import json import json
from unittest.mock import patch from unittest.mock import patch, Mock
from django.test import TestCase, RequestFactory from django.test import TestCase, RequestFactory
from django.urls import reverse from django.urls import reverse

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 OptimerConfig(AppConfig): class OptimerConfig(AppConfig):
name = 'allianceauth.optimer' name = 'allianceauth.optimer'
label = 'optimer' label = 'optimer'
verbose_name = _('Fleet Operations')

View File

@ -14,7 +14,7 @@
<th class="text-center">{% translate "Operation" %}</th> <th class="text-center">{% translate "Operation" %}</th>
<th class="text-center">{% translate "Type" %}</th> <th class="text-center">{% translate "Type" %}</th>
<th class="text-center">{% translate "Form Up System" %}</th> <th class="text-center">{% translate "Form Up System" %}</th>
<th class="text-center">{% translate "EVE Time" %}</th> <th class="text-center">{% translate "EVE time" %}</th>
</tr> </tr>
</thead> </thead>

View File

@ -24,8 +24,8 @@
{% block content %} {% block content %}
<div> <div>
<div class="text-center mb-3"> <div class="text-center mb-3">
<div class="badge bg-primary text-start"> <div class="badge text-bg-primary text-start">
<b>{% translate "Current Eve Time:" %}</b> <b>{% translate "Current EVE time:" %}</b>
<span id="current-time"></span> <span id="current-time"></span>
</div> </div>
</div> </div>
@ -127,8 +127,8 @@
}; };
/** /**
* Get the current Eve time as a string * Get the current EVE time as a string
* @returns {string} Eve time string * @returns {string} EVE time string
*/ */
const updateClock = () => { const updateClock = () => {
document.getElementById("current-time").innerHTML = getCurrentEveTimeString(); document.getElementById("current-time").innerHTML = getCurrentEveTimeString();

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 PermissionsToolConfig(AppConfig): class PermissionsToolConfig(AppConfig):
name = 'allianceauth.permissions_tool' name = 'allianceauth.permissions_tool'
label = 'permissions_tool' label = 'permissions_tool'
verbose_name = _('Permissions Audit')

View File

@ -1,7 +1,7 @@
import logging import logging
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission, User
from django.db.models import Count from django.db.models import Count
from django.shortcuts import render, Http404 from django.shortcuts import render, Http404

View File

@ -15,67 +15,68 @@ from django.contrib import messages
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
INSTALLED_APPS = [ INSTALLED_APPS = [
'allianceauth', # needs to be on top of this list to support favicons in Django admin (see https://gitlab.com/allianceauth/allianceauth/-/issues/1301) "allianceauth", # needs to be on top of this list to support favicons in Django admin (see https://gitlab.com/allianceauth/allianceauth/-/issues/1301)
'django.contrib.admin', "django.contrib.admin",
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.staticfiles', "django.contrib.staticfiles",
'django.contrib.humanize', "django.contrib.humanize",
'django_celery_beat', "django_celery_beat",
'solo', "solo",
'bootstrapform', "bootstrapform",
'django_bootstrap5', # https://github.com/zostera/django-bootstrap5 "django_bootstrap5", # https://github.com/zostera/django-bootstrap5
'sortedm2m', "sortedm2m",
'esi', "esi",
'allianceauth.framework', "allianceauth.framework",
'allianceauth.authentication', "allianceauth.authentication",
'allianceauth.services', "allianceauth.services",
'allianceauth.eveonline', "allianceauth.eveonline",
'allianceauth.groupmanagement', "allianceauth.groupmanagement",
'allianceauth.notifications', "allianceauth.notifications",
'allianceauth.thirdparty.navhelper', "allianceauth.thirdparty.navhelper",
'allianceauth.analytics', "allianceauth.analytics",
'allianceauth.menu', "allianceauth.menu",
'allianceauth.theme', "allianceauth.theme",
'allianceauth.theme.darkly', "allianceauth.theme.darkly",
'allianceauth.theme.flatly', "allianceauth.theme.flatly",
'allianceauth.theme.materia', "allianceauth.theme.materia",
"allianceauth.custom_css", "allianceauth.custom_css",
'allianceauth.crontab', "allianceauth.crontab",
'sri', "sri",
] ]
SRI_ALGORITHM = "sha512" SRI_ALGORITHM = "sha512"
SECRET_KEY = "wow I'm a really bad default secret key" SECRET_KEY = "wow I'm a really bad default secret key"
# Celery configuration # Celery configuration
BROKER_URL = 'redis://localhost:6379/0' BROKER_URL = "redis://localhost:6379/0"
CELERYBEAT_SCHEDULER = "allianceauth.crontab.schedulers.OffsetDatabaseScheduler" CELERYBEAT_SCHEDULER = "allianceauth.crontab.schedulers.OffsetDatabaseScheduler"
CELERYBEAT_SCHEDULE = { CELERYBEAT_SCHEDULE = {
'esi_cleanup_callbackredirect': { "esi_cleanup_callbackredirect": {
'task': 'esi.tasks.cleanup_callbackredirect', "task": "esi.tasks.cleanup_callbackredirect",
'schedule': crontab(minute='0', hour='*/4'), "schedule": crontab(minute="0", hour="*/4"),
}, },
'esi_cleanup_token': { "esi_cleanup_token": {
'task': 'esi.tasks.cleanup_token', "task": "esi.tasks.cleanup_token",
'schedule': crontab(minute='0', hour='0'), "schedule": crontab(minute="0", hour="0"),
"apply_offset": True,
}, },
'run_model_update': { "run_model_update": {
'task': 'allianceauth.eveonline.tasks.run_model_update', "task": "allianceauth.eveonline.tasks.run_model_update",
'schedule': crontab(minute='0', hour="*/6"), "schedule": crontab(minute="0", hour="*/6"),
'apply_offset': True "apply_offset": True,
}, },
'check_all_character_ownership': { "check_all_character_ownership": {
'task': 'allianceauth.authentication.tasks.check_all_character_ownership', "task": "allianceauth.authentication.tasks.check_all_character_ownership",
'schedule': crontab(minute='0', hour='*/4'), "schedule": crontab(minute="0", hour="*/4"),
'apply_offset': True "apply_offset": True,
},
"analytics_daily_stats": {
"task": "allianceauth.analytics.tasks.analytics_daily_stats",
"schedule": crontab(minute="0", hour="2"),
}, },
'analytics_daily_stats': {
'task': 'allianceauth.analytics.tasks.analytics_daily_stats',
'schedule': crontab(minute='0', hour='2'),
}
} }
@ -84,22 +85,20 @@ PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(PROJECT_DIR) BASE_DIR = os.path.dirname(PROJECT_DIR)
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'allianceauth.authentication.middleware.UserSettingsMiddleware', "allianceauth.authentication.middleware.UserSettingsMiddleware",
'django.middleware.locale.LocaleMiddleware', "django.middleware.locale.LocaleMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
ROOT_URLCONF = 'allianceauth.urls' ROOT_URLCONF = "allianceauth.urls"
LOCALE_PATHS = ( LOCALE_PATHS = (os.path.join(BASE_DIR, "locale/"),)
os.path.join(BASE_DIR, 'locale/'),
)
LANGUAGES = ( # Sorted by Language Code alphabetical order + English at top LANGUAGES = ( # Sorted by Language Code alphabetical order + English at top
("en", _("English")), ("en", _("English")),
@ -119,7 +118,12 @@ LANGUAGES = ( # Sorted by Language Code alphabetical order + English at top
# Django's language codes are different from some of the libraries we use, # Django's language codes are different from some of the libraries we use,
# so we need to map them. # so we need to map them.
# When adding a new language, please remember to add it to the mapping
# and add the language files to their respective directories under `allianceauth/static/allianceauth/libs/`.
LANGUAGE_MAPPING = { LANGUAGE_MAPPING = {
# See https://github.com/DataTables/Plugins/tree/master/i18n for available languages
# (We use the JSON files)
# `allianceauth/static/allianceauth/libs/DataTables/Plugins/{version}/i18n/` for the files
"DataTables": { "DataTables": {
"cs-cz": "cs", "cs-cz": "cs",
"de": "de-DE", "de": "de-DE",
@ -134,6 +138,8 @@ LANGUAGE_MAPPING = {
"uk": "uk", "uk": "uk",
"zh-hans": "zh-HANT", "zh-hans": "zh-HANT",
}, },
# See https://github.com/moment/moment/tree/master/locale for available languages
# `allianceauth/static/allianceauth/libs/moment.js/{version}/locale/` for the files
"MomentJS": { "MomentJS": {
"cs-cz": "cs", "cs-cz": "cs",
"de": "de", "de": "de",
@ -152,58 +158,58 @@ LANGUAGE_MAPPING = {
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [os.path.join(PROJECT_DIR, 'templates')], "DIRS": [os.path.join(PROJECT_DIR, "templates")],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
'django.template.context_processors.i18n', "django.template.context_processors.i18n",
'django.template.context_processors.media', "django.template.context_processors.media",
'django.template.context_processors.static', "django.template.context_processors.static",
'django.template.context_processors.tz', "django.template.context_processors.tz",
'allianceauth.context_processors.auth_settings', "allianceauth.context_processors.auth_settings",
], ],
}, },
}, },
] ]
WSGI_APPLICATION = 'allianceauth.wsgi.application' WSGI_APPLICATION = "allianceauth.wsgi.application"
# Password validation # Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
}, },
] ]
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
'allianceauth.authentication.backends.StateBackend', "allianceauth.authentication.backends.StateBackend",
'django.contrib.auth.backends.ModelBackend' "django.contrib.auth.backends.ModelBackend",
] ]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/ # https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = "en-us"
LANGUAGE_COOKIE_AGE = 1209600 LANGUAGE_COOKIE_AGE = 1209600
TIME_ZONE = 'UTC' TIME_ZONE = "UTC"
USE_I18N = True USE_I18N = True
@ -211,44 +217,51 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/ # https://docs.djangoproject.com/en/1.10/howto/static-files/
STATIC_URL = '/static/' STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "allianceauth.framework.staticfiles.storage.AaManifestStaticFilesStorage",
},
}
STATIC_URL = "/static/"
STATICFILES_DIRS = [ STATICFILES_DIRS = [
os.path.join(PROJECT_DIR, 'static'), os.path.join(PROJECT_DIR, "static"),
] ]
STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATIC_ROOT = os.path.join(BASE_DIR, "static")
# Bootstrap messaging css workaround # Bootstrap messaging css workaround
MESSAGE_TAGS = { MESSAGE_TAGS = {messages.ERROR: "danger error"}
messages.ERROR: 'danger error'
}
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django_redis.cache.RedisCache", "BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1" # change the 1 here for the DB used "LOCATION": "redis://127.0.0.1:6379/1", # change the 1 here for the DB used
} }
} }
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ["*"]
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': 'django.db.backends.sqlite3', "ENGINE": "django.db.backends.sqlite3",
'NAME': str(os.path.join(BASE_DIR, 'alliance_auth.sqlite3')), "NAME": str(os.path.join(BASE_DIR, "alliance_auth.sqlite3")),
}, },
} }
SITE_NAME = 'Alliance Auth' SITE_NAME = "Alliance Auth"
DEFAULT_THEME = "allianceauth.theme.flatly.auth_hooks.FlatlyThemeHook" DEFAULT_THEME = "allianceauth.theme.flatly.auth_hooks.FlatlyThemeHook"
DEFAULT_THEME_DARK = "allianceauth.theme.darkly.auth_hooks.DarklyThemeHook" # Legacy AAv3 user.profile.night_mode=1 DEFAULT_THEME_DARK = "allianceauth.theme.darkly.auth_hooks.DarklyThemeHook" # Legacy AAv3 user.profile.night_mode=1
LOGIN_URL = 'auth_login_user' # view that handles login logic LOGIN_URL = "auth_login_user" # view that handles login logic
LOGIN_REDIRECT_URL = 'authentication:dashboard' # default destination when logging in if no redirect specified LOGIN_REDIRECT_URL = "authentication:dashboard" # default destination when logging in if no redirect specified
LOGOUT_REDIRECT_URL = 'authentication:dashboard' # destination after logging out LOGOUT_REDIRECT_URL = "authentication:dashboard" # destination after logging out
# Both of these redirects accept values as per the django redirect shortcut # Both of these redirects accept values as per the django redirect shortcut
# https://docs.djangoproject.com/en/1.11/topics/http/shortcuts/#redirect # https://docs.djangoproject.com/en/1.11/topics/http/shortcuts/#redirect
# - url names eg 'authentication:dashboard' # - url names eg 'authentication:dashboard'
@ -256,73 +269,71 @@ LOGOUT_REDIRECT_URL = 'authentication:dashboard' # destination after logging ou
# - absolute urls eg 'http://example.com/dashboard' # - absolute urls eg 'http://example.com/dashboard'
# scopes required on new tokens when logging in. Cannot be blank. # scopes required on new tokens when logging in. Cannot be blank.
LOGIN_TOKEN_SCOPES = ['publicData'] LOGIN_TOKEN_SCOPES = ["publicData"]
EMAIL_TIMEOUT = 15 EMAIL_TIMEOUT = 15
# number of days email verification links are valid for # number of days email verification links are valid for
ACCOUNT_ACTIVATION_DAYS = 1 ACCOUNT_ACTIVATION_DAYS = 1
ESI_API_URL = 'https://esi.evetech.net/' ESI_API_URL = "https://esi.evetech.net/"
LOGGING = { LOGGING = {
'version': 1, "version": 1,
'disable_existing_loggers': False, "disable_existing_loggers": False,
'formatters': { "formatters": {
'verbose': { "verbose": {
'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s", "format": "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
'datefmt': "%d/%b/%Y %H:%M:%S" "datefmt": "%d/%b/%Y %H:%M:%S",
}, },
'simple': { "simple": {"format": "%(levelname)s %(message)s"},
'format': '%(levelname)s %(message)s' },
"handlers": {
"log_file": {
"level": "INFO", # edit this line to change logging level to file
"class": "logging.handlers.RotatingFileHandler",
"filename": os.path.join(BASE_DIR, "log/allianceauth.log"),
"formatter": "verbose",
"maxBytes": 1024 * 1024 * 5, # edit this line to change max log file size
"backupCount": 5, # edit this line to change number of log backups
},
"extension_file": {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": os.path.join(BASE_DIR, "log/extensions.log"),
"formatter": "verbose",
"maxBytes": 1024 * 1024 * 5, # edit this line to change max log file size
"backupCount": 5, # edit this line to change number of log backups
},
"console": {
"level": "DEBUG", # edit this line to change logging level to console
"class": "logging.StreamHandler",
"formatter": "verbose",
},
"notifications": { # creates notifications for users with logging_notifications permission
"level": "ERROR", # edit this line to change logging level to notifications
"class": "allianceauth.notifications.handlers.NotificationHandler",
"formatter": "verbose",
}, },
}, },
'handlers': { "loggers": {
'log_file': { "allianceauth": {
'level': 'INFO', # edit this line to change logging level to file "handlers": ["log_file", "console", "notifications"],
'class': 'logging.handlers.RotatingFileHandler', "level": "DEBUG",
'filename': os.path.join(BASE_DIR, 'log/allianceauth.log'),
'formatter': 'verbose',
'maxBytes': 1024 * 1024 * 5, # edit this line to change max log file size
'backupCount': 5, # edit this line to change number of log backups
}, },
'extension_file': { "extensions": {
'level': 'INFO', "handlers": ["extension_file", "console"],
'class': 'logging.handlers.RotatingFileHandler', "level": "DEBUG",
'filename': os.path.join(BASE_DIR, 'log/extensions.log'),
'formatter': 'verbose',
'maxBytes': 1024 * 1024 * 5, # edit this line to change max log file size
'backupCount': 5, # edit this line to change number of log backups
}, },
'console': { "django": {
'level': 'DEBUG', # edit this line to change logging level to console "handlers": ["log_file", "console"],
'class': 'logging.StreamHandler', "level": "ERROR",
'formatter': 'verbose',
}, },
'notifications': { # creates notifications for users with logging_notifications permission "esi": {
'level': 'ERROR', # edit this line to change logging level to notifications "handlers": ["log_file", "console"],
'class': 'allianceauth.notifications.handlers.NotificationHandler', "level": "DEBUG",
'formatter': 'verbose',
}, },
}, },
'loggers': {
'allianceauth': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'extensions': {
'handlers': ['extension_file', 'console'],
'level': 'DEBUG',
},
'django': {
'handlers': ['log_file', 'console'],
'level': 'ERROR',
},
'esi': {
'handlers': ['log_file', 'console'],
'level': 'DEBUG',
},
}
} }
DEFAULT_AUTO_FIELD = "django.db.models.AutoField" DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

View File

@ -1,49 +1,48 @@
[program:beat] [program:beat]
command={{ celery }} -A {{ project_name }} beat command = {{ celery }} -A {{ project_name }} beat
directory={{ project_directory }} directory = {{ project_directory }}
user=allianceserver user = allianceserver
stdout_logfile={{ project_directory }}/log/beat.log stdout_logfile = {{ project_directory }}/log/%(program_name)s.log
stderr_logfile={{ project_directory }}/log/beat.log stderr_logfile = {{ project_directory }}/log/%(program_name)s.log
autostart=true autostart = true
autorestart=true autorestart = true
startsecs=10 startsecs = 10
priority=998 priority = 998
[program:worker] [program:worker]
command={{ celery }} -A {{ project_name }} worker --pool=threads --concurrency=5 -n %(program_name)s_%(process_num)02d command = {{ celery }} -A {{ project_name }} worker --pool=threads --concurrency=5 -n %(program_name)s_%(process_num)02d
directory={{ project_directory }} directory = {{ project_directory }}
user=allianceserver user = allianceserver
numprocs=1 numprocs = 1
process_name=%(program_name)s_%(process_num)02d process_name = %(program_name)s_%(process_num)02d
stdout_logfile={{ project_directory }}/log/worker.log stdout_logfile = {{ project_directory }}/log/%(program_name)s.log
stderr_logfile={{ project_directory }}/log/worker.log stderr_logfile = {{ project_directory }}/log/%(program_name)s.log
autostart=true autostart = true
autorestart=true autorestart = true
startsecs=10 startsecs = 10
stopwaitsecs = 600 stopwaitsecs = 600
killasgroup=true killasgroup = true
priority=998 priority = 998
{% if gunicorn %} {% if gunicorn %}
[program:gunicorn] [program:gunicorn]
user = allianceserver user = allianceserver
directory={{ project_directory }} directory = {{ project_directory }}
command={{ gunicorn }} {{ project_name }}.wsgi --workers=3 --timeout 120 command = {{ gunicorn }} {{ project_name }}.wsgi --workers=3 --timeout 120
stdout_logfile={{ project_directory }}/log/gunicorn.log stdout_logfile = {{ project_directory }}/log/%(program_name)s.log
stderr_logfile={{ project_directory }}/log/gunicorn.log stderr_logfile = {{ project_directory }}/log/%(program_name)s.log
autostart=true autostart = true
autorestart=true autorestart = true
stopsignal=INT stopsignal = INT
{% endif %} {% endif %}
[eventlistener:memmon] [eventlistener:memmon]
command={{ memmon }} -p worker_00=256MB -p gunicorn=256MB command = {{ memmon }} -p worker_00=256MB -p gunicorn=256MB
directory={{ project_directory }} directory = {{ project_directory }}
events=TICK_60 events = TICK_60
stdout_logfile={{ project_directory }}/log/memmon.log stdout_logfile = {{ project_directory }}/log/memmon.log
stderr_logfile={{ project_directory }}/log/memmon.log stderr_logfile = {{ project_directory }}/log/memmon.log
[group:{{ project_name }}] [group:{{ project_name }}]
programs=beat,worker{% if gunicorn %},gunicorn{% endif %} programs = beat,worker{% if gunicorn %},gunicorn{% endif %}
priority=999 priority = 999

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 ServicesConfig(AppConfig): class ServicesConfig(AppConfig):
name = 'allianceauth.services' name = 'allianceauth.services'
label = 'services' label = 'services'
verbose_name = _('Services')
def ready(self): def ready(self):
from . import signals from . import signals

View File

@ -1,6 +1,13 @@
from string import Formatter from string import Formatter
from django.urls import include, re_path
from typing import Iterable, Optional from typing import Iterable, Optional
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.template.loader import render_to_string
from django.urls import include, re_path
from django.utils.functional import cached_property
from allianceauth.hooks import get_hooks from allianceauth.hooks import get_hooks
from allianceauth.menu.hooks import MenuItemHook from allianceauth.menu.hooks import MenuItemHook
from django.conf import settings from django.conf import settings

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 DiscordServiceConfig(AppConfig): class DiscordServiceConfig(AppConfig):
name = 'allianceauth.services.modules.discord' name = 'allianceauth.services.modules.discord'
label = 'discord' label = 'discord'
verbose_name = _('Discord Service')

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