mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-05 14:46:20 +01:00
Compare commits
221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e14ea4573 | ||
|
|
c743eca0f7 | ||
|
|
2002f24178 | ||
|
|
6412aedf53 | ||
|
|
939df08b95 | ||
|
|
d8506aa753 | ||
|
|
3f2cdac658 | ||
|
|
d57ab01ff3 | ||
|
|
91b62bbe9d | ||
|
|
557a52e3c8 | ||
|
|
f8fefd92a5 | ||
|
|
f2c43ee921 | ||
|
|
99945b0146 | ||
|
|
abb9dc4db6 | ||
|
|
eba5b80cde | ||
|
|
5b39c887a5 | ||
|
|
183363e789 | ||
|
|
d8704f4d8f | ||
|
|
165ee44a63 | ||
|
|
e8f508cecb | ||
|
|
3044f18900 | ||
|
|
79637020f3 | ||
|
|
2d34422e2d | ||
|
|
6b932b1188 | ||
|
|
f62153c746 | ||
|
|
88216c3f81 | ||
|
|
dc983c31e3 | ||
|
|
4204c44bde | ||
|
|
8da0122d17 | ||
|
|
c9fcf6e6bf | ||
|
|
36866cc59b | ||
|
|
298bdd98ed | ||
|
|
819018748d | ||
|
|
32e8e0fdd0 | ||
|
|
7625060a12 | ||
|
|
672cb13bfe | ||
|
|
7170f75b89 | ||
|
|
8f60c7a00a | ||
|
|
34ae6e402c | ||
|
|
0905e48994 | ||
|
|
02fcf7d500 | ||
|
|
8d8da50946 | ||
|
|
c1499d173f | ||
|
|
b149baa4e5 | ||
|
|
4807c69b5e | ||
|
|
ebefa0e307 | ||
|
|
468e7433f9 | ||
|
|
3ca313f907 | ||
|
|
820065fc04 | ||
|
|
3eddeefe28 | ||
|
|
82d7d7e3bf | ||
|
|
93194b4f2d | ||
|
|
fa335253d3 | ||
|
|
d1af9416b3 | ||
|
|
f4ac2ea400 | ||
|
|
31c1f8bb7d | ||
|
|
57f7178f1e | ||
|
|
17d4a4c415 | ||
|
|
18ce433fa0 | ||
|
|
1eadb1d934 | ||
|
|
59a8f8a967 | ||
|
|
2dc07b5519 | ||
|
|
3454520dfe | ||
|
|
7a195d4158 | ||
|
|
b73072dec0 | ||
|
|
1ca5e38bd9 | ||
|
|
ecb737c6a5 | ||
|
|
7063f53cdf | ||
|
|
017424b9d4 | ||
|
|
f6c26cf2ec | ||
|
|
9a422bd4ca | ||
|
|
47fec23f2e | ||
|
|
399ef1917d | ||
|
|
9db443ba54 | ||
|
|
0f2f5ea0ba | ||
|
|
1f781c5037 | ||
|
|
36dedfcbd2 | ||
|
|
13a05606fb | ||
|
|
90ad7790e1 | ||
|
|
6b8341ab5a | ||
|
|
d15f42b3fd | ||
|
|
cc60b26f5a | ||
|
|
36ff0af993 | ||
|
|
f17c94a9e1 | ||
|
|
7e3ba476f3 | ||
|
|
dd1313a2a9 | ||
|
|
763003bd7d | ||
|
|
f3217443dd | ||
|
|
a713ae1914 | ||
|
|
5815bac0df | ||
|
|
6154d2c2e7 | ||
|
|
b34661b35d | ||
|
|
a9a7e03b80 | ||
|
|
23c797ef64 | ||
|
|
da102618a0 | ||
|
|
51ee281b14 | ||
|
|
9133232c20 | ||
|
|
9cbabee126 | ||
|
|
4026523a2e | ||
|
|
7fbf96623b | ||
|
|
273bda173e | ||
|
|
7bd5838ea1 | ||
|
|
b232d9ab17 | ||
|
|
a11b870664 | ||
|
|
a27aae5d1c | ||
|
|
117ef63d90 | ||
|
|
1bde3d5672 | ||
|
|
d2355b1ec8 | ||
|
|
191d474a8e | ||
|
|
ec9a9733be | ||
|
|
cf7a8cedf1 | ||
|
|
18cbb994d5 | ||
|
|
663388a0c2 | ||
|
|
7a943591ec | ||
|
|
cd189927fe | ||
|
|
8772349309 | ||
|
|
cf20100cb5 | ||
|
|
9b9c2ddc04 | ||
|
|
34839e8344 | ||
|
|
89ef4f4cbc | ||
|
|
2cc7f46aae | ||
|
|
8d255fb720 | ||
|
|
67cf68ad87 | ||
|
|
db1971d4c2 | ||
|
|
63c1521cba | ||
|
|
ba7ef11505 | ||
|
|
d2e494b9be | ||
|
|
98bab0b180 | ||
|
|
c4efb2a11f | ||
|
|
94e4895f29 | ||
|
|
70eb1b5b50 | ||
|
|
e247a94db3 | ||
|
|
714431c932 | ||
|
|
b026277ab0 | ||
|
|
11855f0b54 | ||
|
|
635fbfe2c8 | ||
|
|
b10233daf0 | ||
|
|
1aa3187491 | ||
|
|
59f17a88f0 | ||
|
|
75db3195d4 | ||
|
|
afe3fea757 | ||
|
|
1072c00a28 | ||
|
|
b221c1ce24 | ||
|
|
1617c775ee | ||
|
|
cc88a02001 | ||
|
|
ae5d0f4a2f | ||
|
|
067e2c424e | ||
|
|
d64675a3b0 | ||
|
|
17a6b3225e | ||
|
|
b83f591dc2 | ||
|
|
be2fbe862b | ||
|
|
f95bee0921 | ||
|
|
2f9ae8b054 | ||
|
|
74651dd30a | ||
|
|
9cdcd8365c | ||
|
|
f5d70a2c48 | ||
|
|
f40ebbfba4 | ||
|
|
2551a9dd64 | ||
|
|
e3b01ccbc9 | ||
|
|
267a392945 | ||
|
|
634d021bf2 | ||
|
|
4e8bfba738 | ||
|
|
297f98f046 | ||
|
|
27dad05927 | ||
|
|
697e9dd772 | ||
|
|
312951ea3f | ||
|
|
e4bf96cfb6 | ||
|
|
3bd6baa8f9 | ||
|
|
06e38dcd93 | ||
|
|
f47b9eee5b | ||
|
|
0d4cab66b2 | ||
|
|
dc23ee8ad2 | ||
|
|
65f2efc890 | ||
|
|
def30900b4 | ||
|
|
d7fabccddd | ||
|
|
45289e1d17 | ||
|
|
e7bafaa4d8 | ||
|
|
ba3f1507be | ||
|
|
7b9bf08aa3 | ||
|
|
360458f574 | ||
|
|
def6431052 | ||
|
|
a47bd8d7c7 | ||
|
|
22a270aedb | ||
|
|
54bce4315b | ||
|
|
c930f7bbeb | ||
|
|
8c1f06d7b8 | ||
|
|
815b6fa030 | ||
|
|
7c05217900 | ||
|
|
64ee273953 | ||
|
|
d8c2944966 | ||
|
|
7669c9e55d | ||
|
|
71c9faaf28 | ||
|
|
236c70316c | ||
|
|
0d0686f58a | ||
|
|
3706a1aedf | ||
|
|
47f1b77320 | ||
|
|
8dec242a93 | ||
|
|
6b934060dd | ||
|
|
ff88a16163 | ||
|
|
e81a66b74b | ||
|
|
2ff200c566 | ||
|
|
091a2637ea | ||
|
|
6c7729308c | ||
|
|
0195ef23d5 | ||
|
|
a7afa4a0c3 | ||
|
|
004100091f | ||
|
|
20231ce198 | ||
|
|
0851a6d085 | ||
|
|
0cd36ad5bc | ||
|
|
7618dd0f91 | ||
|
|
cf49a2cb65 | ||
|
|
cbdce18633 | ||
|
|
a0d14eb1d3 | ||
|
|
53ce4d2453 | ||
|
|
1ddb041d6d | ||
|
|
43cbfd1c47 | ||
|
|
b9a8495a43 | ||
|
|
e296477880 | ||
|
|
bd5c2d8cbc | ||
|
|
ccd40d5c68 | ||
|
|
113977b19f |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -69,11 +69,7 @@ celerybeat-schedule
|
|||||||
#gitlab configs
|
#gitlab configs
|
||||||
.gitlab/
|
.gitlab/
|
||||||
|
|
||||||
#transifex
|
|
||||||
.tx/
|
|
||||||
|
|
||||||
#other
|
#other
|
||||||
.flake8
|
.flake8
|
||||||
.pylintrc
|
.pylintrc
|
||||||
Makefile
|
Makefile
|
||||||
.isort.cfg
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ before_script:
|
|||||||
pre-commit-check:
|
pre-commit-check:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
stage: pre-commit
|
stage: pre-commit
|
||||||
image: python:3.8-bullseye
|
image: python:3.10-bullseye
|
||||||
variables:
|
variables:
|
||||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||||
cache:
|
cache:
|
||||||
@@ -89,7 +89,7 @@ test-3.10-core:
|
|||||||
|
|
||||||
test-3.11-core:
|
test-3.11-core:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
image: python:3.11-rc-bullseye
|
image: python:3.11-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py311-core
|
- tox -e py311-core
|
||||||
artifacts:
|
artifacts:
|
||||||
@@ -98,6 +98,18 @@ test-3.11-core:
|
|||||||
coverage_report:
|
coverage_report:
|
||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
|
|
||||||
|
test-pvpy-core:
|
||||||
|
<<: *only-default
|
||||||
|
image: pypy:3.9-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e pypy-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
coverage_report:
|
||||||
|
coverage_format: cobertura
|
||||||
|
path: coverage.xml
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
test-3.8-all:
|
test-3.8-all:
|
||||||
@@ -138,7 +150,7 @@ test-3.10-all:
|
|||||||
|
|
||||||
test-3.11-all:
|
test-3.11-all:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
image: python:3.11-rc-bullseye
|
image: python:3.11-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py311-all
|
- tox -e py311-all
|
||||||
artifacts:
|
artifacts:
|
||||||
@@ -147,6 +159,19 @@ test-3.11-all:
|
|||||||
coverage_report:
|
coverage_report:
|
||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
|
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
|
||||||
|
|
||||||
|
test-pvpy-all:
|
||||||
|
<<: *only-default
|
||||||
|
image: pypy:3.9-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e pypy-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
coverage_report:
|
||||||
|
coverage_format: cobertura
|
||||||
|
path: coverage.xml
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
build-test:
|
build-test:
|
||||||
@@ -213,6 +238,8 @@ build-image:
|
|||||||
docker image push --all-tags $CI_REGISTRY_IMAGE/auth
|
docker image push --all-tags $CI_REGISTRY_IMAGE/auth
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG
|
- if: $CI_COMMIT_TAG
|
||||||
|
when: delayed
|
||||||
|
start_in: 10 minutes
|
||||||
|
|
||||||
build-image-dev:
|
build-image-dev:
|
||||||
before_script: []
|
before_script: []
|
||||||
|
|||||||
6
.isort.cfg
Normal file
6
.isort.cfg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[settings]
|
||||||
|
profile=django
|
||||||
|
sections=FUTURE,STDLIB,THIRDPARTY,DJANGO,ESI,FIRSTPARTY,LOCALFOLDER
|
||||||
|
known_esi=esi
|
||||||
|
known_django=django
|
||||||
|
skip_gitignore=true
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.3.0
|
rev: v4.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
- id: check-json
|
- id: check-json
|
||||||
@@ -13,27 +13,49 @@ repos:
|
|||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: fix-byte-order-marker
|
- id: fix-byte-order-marker
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
exclude: (\.min\.css|\.min\.js|\.mo|\.po|swagger\.json)$
|
exclude: |
|
||||||
|
(?x)(
|
||||||
|
\.min\.css|
|
||||||
|
\.min\.js|
|
||||||
|
\.po|
|
||||||
|
\.mo|
|
||||||
|
swagger\.json
|
||||||
|
)
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
exclude: (\.min\.css|\.min\.js|\.mo|\.po|swagger\.json)$
|
exclude: |
|
||||||
|
(?x)(
|
||||||
|
\.min\.css|
|
||||||
|
\.min\.js|
|
||||||
|
\.po|
|
||||||
|
\.mo|
|
||||||
|
swagger\.json
|
||||||
|
)
|
||||||
- id: mixed-line-ending
|
- id: mixed-line-ending
|
||||||
args: [ '--fix=lf' ]
|
args: [ '--fix=lf' ]
|
||||||
- id: fix-encoding-pragma
|
- id: fix-encoding-pragma
|
||||||
args: [ '--remove' ]
|
args: [ '--remove' ]
|
||||||
|
|
||||||
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
||||||
rev: 2.4.0
|
rev: 2.7.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: editorconfig-checker
|
- id: editorconfig-checker
|
||||||
exclude: ^(LICENSE|allianceauth\/static\/css\/themes\/bootstrap-locals.less|allianceauth\/eveonline\/swagger.json|(.*.po)|(.*.mo))
|
exclude: |
|
||||||
|
(?x)(
|
||||||
|
LICENSE|
|
||||||
|
allianceauth\/static\/allianceauth\/css\/themes\/bootstrap-locals.less|
|
||||||
|
\.po|
|
||||||
|
\.mo|
|
||||||
|
swagger\.json
|
||||||
|
)
|
||||||
|
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v2.34.0
|
rev: v3.3.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [ --py38-plus ]
|
args: [ --py38-plus ]
|
||||||
|
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v1.20.1
|
rev: v2.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
|
args: [ --include-version-classifiers ]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ build:
|
|||||||
apt_packages:
|
apt_packages:
|
||||||
- redis
|
- redis
|
||||||
tools:
|
tools:
|
||||||
python: "3.10"
|
python: "3.8"
|
||||||
|
|
||||||
# Build documentation in the docs/ directory with Sphinx
|
# Build documentation in the docs/ directory with Sphinx
|
||||||
sphinx:
|
sphinx:
|
||||||
|
|||||||
10
.tx/config
Normal file
10
.tx/config
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[main]
|
||||||
|
host = https://www.transifex.com
|
||||||
|
lang_map = zh-Hans: zh_Hans
|
||||||
|
|
||||||
|
[o:alliance-auth:p:alliance-auth:r:django-po]
|
||||||
|
file_filter = allianceauth/locale/<lang>/LC_MESSAGES/django.po
|
||||||
|
source_file = allianceauth/locale/en/LC_MESSAGES/django.po
|
||||||
|
source_lang = en
|
||||||
|
type = PO
|
||||||
|
minimum_perc = 0
|
||||||
10
.tx/config_20230406134150.bak
Executable file
10
.tx/config_20230406134150.bak
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
[main]
|
||||||
|
host = https://www.transifex.com
|
||||||
|
lang_map = zh-Hans:zh_Hans
|
||||||
|
|
||||||
|
[alliance-auth.django-po]
|
||||||
|
file_filter = allianceauth/locale/<lang>/LC_MESSAGES/django.po
|
||||||
|
minimum_perc = 0
|
||||||
|
source_file = allianceauth/locale/en/LC_MESSAGES/django.po
|
||||||
|
source_lang = en
|
||||||
|
type = PO
|
||||||
@@ -36,7 +36,7 @@ Main features:
|
|||||||
|
|
||||||
- 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)
|
||||||
|
|
||||||
- English :flag_gb:, Chinese :flag_cn:, German :flag_de:, Spanish :flag_es:, Korean :flag_kr: and Russian :flag_ru: localization
|
- English :flag_gb:, Chinese :flag_cn:, German :flag_de:, Spanish :flag_es:, Korean :flag_kr:, Russian :flag_ru:, Italian :flag_it:, French :flag_fr:, Japanese :flag_jp: and Ukrainian :flag_ua: Localization
|
||||||
|
|
||||||
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](http://allianceauth.rtfd.io).
|
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](http://allianceauth.rtfd.io).
|
||||||
|
|
||||||
@@ -56,13 +56,15 @@ Here is an example of the Alliance Auth web site with some plug-ins apps and ser
|
|||||||
|
|
||||||
- [Aaron Kable](https://gitlab.com/aaronkable/)
|
- [Aaron Kable](https://gitlab.com/aaronkable/)
|
||||||
- [Ariel Rin](https://gitlab.com/soratidus999/)
|
- [Ariel Rin](https://gitlab.com/soratidus999/)
|
||||||
- [Basraah](https://gitlab.com/basraah/)
|
|
||||||
- [Col Crunch](https://gitlab.com/colcrunch/)
|
- [Col Crunch](https://gitlab.com/colcrunch/)
|
||||||
- [Erik Kalkoken](https://gitlab.com/ErikKalkoken/)
|
- [Erik Kalkoken](https://gitlab.com/ErikKalkoken/)
|
||||||
|
- [Rounon Dax](https://gitlab.com/ppfeufer)
|
||||||
|
- [snipereagle1](https://gitlab.com/mckernanin)
|
||||||
|
|
||||||
### Former Developers
|
### Former Developers
|
||||||
|
|
||||||
- [Adarnof](https://gitlab.com/adarnof/)
|
- [Adarnof](https://gitlab.com/adarnof/)
|
||||||
|
- [Basraah](https://gitlab.com/basraah/)
|
||||||
|
|
||||||
### Beta Testers / Bug Fixers
|
### Beta Testers / Bug Fixers
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# 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__ = '3.0.0b1'
|
__version__ = '3.5.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__}'
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ from uuid import uuid4
|
|||||||
class AnalyticsIdentifier(models.Model):
|
class AnalyticsIdentifier(models.Model):
|
||||||
|
|
||||||
identifier = models.UUIDField(default=uuid4,
|
identifier = models.UUIDField(default=uuid4,
|
||||||
editable=False)
|
editable=False)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.pk and AnalyticsIdentifier.objects.exists():
|
if not self.pk and AnalyticsIdentifier.objects.exists():
|
||||||
# Force a single object
|
# Force a single object
|
||||||
raise ValidationError('There is can be only one \
|
raise ValidationError('There is can be only one \
|
||||||
AnalyticsIdentifier instance')
|
AnalyticsIdentifier instance')
|
||||||
self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1
|
self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1
|
||||||
return super().save(*args, **kwargs)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ from urllib.parse import parse_qs
|
|||||||
|
|
||||||
import requests_mock
|
import requests_mock
|
||||||
|
|
||||||
from django.test import TestCase, override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from allianceauth.analytics.tasks import ANALYTICS_URL
|
from allianceauth.analytics.tasks import ANALYTICS_URL
|
||||||
from allianceauth.eveonline.tasks import update_character
|
from allianceauth.eveonline.tasks import update_character
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
from allianceauth.utils.testing import NoSocketsTestCase
|
||||||
|
|
||||||
|
|
||||||
@override_settings(CELERY_ALWAYS_EAGER=True)
|
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||||
@requests_mock.mock()
|
@requests_mock.mock()
|
||||||
class TestAnalyticsForViews(TestCase):
|
class TestAnalyticsForViews(NoSocketsTestCase):
|
||||||
@override_settings(ANALYTICS_DISABLED=False)
|
@override_settings(ANALYTICS_DISABLED=False)
|
||||||
def test_should_run_analytics(self, requests_mocker):
|
def test_should_run_analytics(self, requests_mocker):
|
||||||
# given
|
# given
|
||||||
@@ -40,7 +41,7 @@ class TestAnalyticsForViews(TestCase):
|
|||||||
|
|
||||||
@override_settings(CELERY_ALWAYS_EAGER=True)
|
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||||
@requests_mock.mock()
|
@requests_mock.mock()
|
||||||
class TestAnalyticsForTasks(TestCase):
|
class TestAnalyticsForTasks(NoSocketsTestCase):
|
||||||
@override_settings(ANALYTICS_DISABLED=False)
|
@override_settings(ANALYTICS_DISABLED=False)
|
||||||
@patch("allianceauth.eveonline.models.EveCharacter.objects.update_character")
|
@patch("allianceauth.eveonline.models.EveCharacter.objects.update_character")
|
||||||
def test_should_run_analytics_for_successful_task(
|
def test_should_run_analytics_for_successful_task(
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
|
import requests_mock
|
||||||
|
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
from allianceauth.analytics.tasks import (
|
from allianceauth.analytics.tasks import (
|
||||||
analytics_event,
|
analytics_event,
|
||||||
send_ga_tracking_celery_event,
|
send_ga_tracking_celery_event,
|
||||||
send_ga_tracking_web_view)
|
send_ga_tracking_web_view)
|
||||||
from django.test.testcases import TestCase
|
from allianceauth.utils.testing import NoSocketsTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestAnalyticsTasks(TestCase):
|
GOOGLE_ANALYTICS_DEBUG_URL = 'https://www.google-analytics.com/debug/collect'
|
||||||
def test_analytics_event(self):
|
|
||||||
|
|
||||||
|
@override_settings(CELERY_ALWAYS_EAGER=True, CELERY_EAGER_PROPAGATES_EXCEPTIONS=True)
|
||||||
|
@requests_mock.Mocker()
|
||||||
|
class TestAnalyticsTasks(NoSocketsTestCase):
|
||||||
|
def test_analytics_event(self, requests_mocker):
|
||||||
|
requests_mocker.register_uri('POST', GOOGLE_ANALYTICS_DEBUG_URL)
|
||||||
analytics_event(
|
analytics_event(
|
||||||
category='allianceauth.analytics',
|
category='allianceauth.analytics',
|
||||||
action='send_tests',
|
action='send_tests',
|
||||||
@@ -14,15 +24,19 @@ class TestAnalyticsTasks(TestCase):
|
|||||||
value=1,
|
value=1,
|
||||||
event_type='Stats')
|
event_type='Stats')
|
||||||
|
|
||||||
def test_send_ga_tracking_web_view_sent(self):
|
def test_send_ga_tracking_web_view_sent(self, requests_mocker):
|
||||||
# This test sends if the event SENDS to google
|
"""This test sends if the event SENDS to google.
|
||||||
# Not if it was successful
|
Not if it was successful.
|
||||||
|
"""
|
||||||
|
# given
|
||||||
|
requests_mocker.register_uri('POST', GOOGLE_ANALYTICS_DEBUG_URL)
|
||||||
tracking_id = 'UA-186249766-2'
|
tracking_id = 'UA-186249766-2'
|
||||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||||
page = '/index/'
|
page = '/index/'
|
||||||
title = 'Hello World'
|
title = 'Hello World'
|
||||||
locale = 'en'
|
locale = 'en'
|
||||||
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||||
|
# when
|
||||||
response = send_ga_tracking_web_view(
|
response = send_ga_tracking_web_view(
|
||||||
tracking_id,
|
tracking_id,
|
||||||
client_id,
|
client_id,
|
||||||
@@ -30,15 +44,23 @@ class TestAnalyticsTasks(TestCase):
|
|||||||
title,
|
title,
|
||||||
locale,
|
locale,
|
||||||
useragent)
|
useragent)
|
||||||
|
# then
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_send_ga_tracking_web_view_success(self):
|
def test_send_ga_tracking_web_view_success(self, requests_mocker):
|
||||||
|
# given
|
||||||
|
requests_mocker.register_uri(
|
||||||
|
'POST',
|
||||||
|
GOOGLE_ANALYTICS_DEBUG_URL,
|
||||||
|
json={"hitParsingResult":[{'valid': True}]}
|
||||||
|
)
|
||||||
tracking_id = 'UA-186249766-2'
|
tracking_id = 'UA-186249766-2'
|
||||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||||
page = '/index/'
|
page = '/index/'
|
||||||
title = 'Hello World'
|
title = 'Hello World'
|
||||||
locale = 'en'
|
locale = 'en'
|
||||||
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||||
|
# when
|
||||||
json_response = send_ga_tracking_web_view(
|
json_response = send_ga_tracking_web_view(
|
||||||
tracking_id,
|
tracking_id,
|
||||||
client_id,
|
client_id,
|
||||||
@@ -46,15 +68,42 @@ class TestAnalyticsTasks(TestCase):
|
|||||||
title,
|
title,
|
||||||
locale,
|
locale,
|
||||||
useragent).json()
|
useragent).json()
|
||||||
|
# then
|
||||||
self.assertTrue(json_response["hitParsingResult"][0]["valid"])
|
self.assertTrue(json_response["hitParsingResult"][0]["valid"])
|
||||||
|
|
||||||
def test_send_ga_tracking_web_view_invalid_token(self):
|
def test_send_ga_tracking_web_view_invalid_token(self, requests_mocker):
|
||||||
|
# given
|
||||||
|
requests_mocker.register_uri(
|
||||||
|
'POST',
|
||||||
|
GOOGLE_ANALYTICS_DEBUG_URL,
|
||||||
|
json={
|
||||||
|
"hitParsingResult":[
|
||||||
|
{
|
||||||
|
'valid': False,
|
||||||
|
'parserMessage': [
|
||||||
|
{
|
||||||
|
'messageType': 'INFO',
|
||||||
|
'description': 'IP Address from this hit was anonymized to 1.132.110.0.',
|
||||||
|
'messageCode': 'VALUE_MODIFIED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'messageType': 'ERROR',
|
||||||
|
'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.",
|
||||||
|
'messageCode': 'VALUE_INVALID', 'parameter': 'tid'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
tracking_id = 'UA-IntentionallyBadTrackingID-2'
|
tracking_id = 'UA-IntentionallyBadTrackingID-2'
|
||||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||||
page = '/index/'
|
page = '/index/'
|
||||||
title = 'Hello World'
|
title = 'Hello World'
|
||||||
locale = 'en'
|
locale = 'en'
|
||||||
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||||
|
# when
|
||||||
json_response = send_ga_tracking_web_view(
|
json_response = send_ga_tracking_web_view(
|
||||||
tracking_id,
|
tracking_id,
|
||||||
client_id,
|
client_id,
|
||||||
@@ -62,18 +111,25 @@ class TestAnalyticsTasks(TestCase):
|
|||||||
title,
|
title,
|
||||||
locale,
|
locale,
|
||||||
useragent).json()
|
useragent).json()
|
||||||
|
# then
|
||||||
self.assertFalse(json_response["hitParsingResult"][0]["valid"])
|
self.assertFalse(json_response["hitParsingResult"][0]["valid"])
|
||||||
self.assertEqual(json_response["hitParsingResult"][0]["parserMessage"][1]["description"], "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.")
|
self.assertEqual(
|
||||||
|
json_response["hitParsingResult"][0]["parserMessage"][1]["description"],
|
||||||
|
"The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details."
|
||||||
|
)
|
||||||
|
|
||||||
# [{'valid': False, 'parserMessage': [{'messageType': 'INFO', 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', 'messageCode': 'VALUE_MODIFIED'}, {'messageType': 'ERROR', 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", 'messageCode': 'VALUE_INVALID', 'parameter': 'tid'}], 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'}]
|
# [{'valid': False, 'parserMessage': [{'messageType': 'INFO', 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', 'messageCode': 'VALUE_MODIFIED'}, {'messageType': 'ERROR', 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", 'messageCode': 'VALUE_INVALID', 'parameter': 'tid'}], 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'}]
|
||||||
|
|
||||||
def test_send_ga_tracking_celery_event_sent(self):
|
def test_send_ga_tracking_celery_event_sent(self, requests_mocker):
|
||||||
|
# given
|
||||||
|
requests_mocker.register_uri('POST', GOOGLE_ANALYTICS_DEBUG_URL)
|
||||||
tracking_id = 'UA-186249766-2'
|
tracking_id = 'UA-186249766-2'
|
||||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||||
category = 'test'
|
category = 'test'
|
||||||
action = 'test'
|
action = 'test'
|
||||||
label = 'test'
|
label = 'test'
|
||||||
value = '1'
|
value = '1'
|
||||||
|
# when
|
||||||
response = send_ga_tracking_celery_event(
|
response = send_ga_tracking_celery_event(
|
||||||
tracking_id,
|
tracking_id,
|
||||||
client_id,
|
client_id,
|
||||||
@@ -81,15 +137,23 @@ class TestAnalyticsTasks(TestCase):
|
|||||||
action,
|
action,
|
||||||
label,
|
label,
|
||||||
value)
|
value)
|
||||||
|
# then
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_send_ga_tracking_celery_event_success(self):
|
def test_send_ga_tracking_celery_event_success(self, requests_mocker):
|
||||||
|
# given
|
||||||
|
requests_mocker.register_uri(
|
||||||
|
'POST',
|
||||||
|
GOOGLE_ANALYTICS_DEBUG_URL,
|
||||||
|
json={"hitParsingResult":[{'valid': True}]}
|
||||||
|
)
|
||||||
tracking_id = 'UA-186249766-2'
|
tracking_id = 'UA-186249766-2'
|
||||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||||
category = 'test'
|
category = 'test'
|
||||||
action = 'test'
|
action = 'test'
|
||||||
label = 'test'
|
label = 'test'
|
||||||
value = '1'
|
value = '1'
|
||||||
|
# when
|
||||||
json_response = send_ga_tracking_celery_event(
|
json_response = send_ga_tracking_celery_event(
|
||||||
tracking_id,
|
tracking_id,
|
||||||
client_id,
|
client_id,
|
||||||
@@ -97,15 +161,42 @@ class TestAnalyticsTasks(TestCase):
|
|||||||
action,
|
action,
|
||||||
label,
|
label,
|
||||||
value).json()
|
value).json()
|
||||||
|
# then
|
||||||
self.assertTrue(json_response["hitParsingResult"][0]["valid"])
|
self.assertTrue(json_response["hitParsingResult"][0]["valid"])
|
||||||
|
|
||||||
def test_send_ga_tracking_celery_event_invalid_token(self):
|
def test_send_ga_tracking_celery_event_invalid_token(self, requests_mocker):
|
||||||
|
# given
|
||||||
|
requests_mocker.register_uri(
|
||||||
|
'POST',
|
||||||
|
GOOGLE_ANALYTICS_DEBUG_URL,
|
||||||
|
json={
|
||||||
|
"hitParsingResult":[
|
||||||
|
{
|
||||||
|
'valid': False,
|
||||||
|
'parserMessage': [
|
||||||
|
{
|
||||||
|
'messageType': 'INFO',
|
||||||
|
'description': 'IP Address from this hit was anonymized to 1.132.110.0.',
|
||||||
|
'messageCode': 'VALUE_MODIFIED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'messageType': 'ERROR',
|
||||||
|
'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.",
|
||||||
|
'messageCode': 'VALUE_INVALID', 'parameter': 'tid'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
tracking_id = 'UA-IntentionallyBadTrackingID-2'
|
tracking_id = 'UA-IntentionallyBadTrackingID-2'
|
||||||
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
client_id = 'ab33e241fbf042b6aa77c7655a768af7'
|
||||||
category = 'test'
|
category = 'test'
|
||||||
action = 'test'
|
action = 'test'
|
||||||
label = 'test'
|
label = 'test'
|
||||||
value = '1'
|
value = '1'
|
||||||
|
# when
|
||||||
json_response = send_ga_tracking_celery_event(
|
json_response = send_ga_tracking_celery_event(
|
||||||
tracking_id,
|
tracking_id,
|
||||||
client_id,
|
client_id,
|
||||||
@@ -113,7 +204,9 @@ class TestAnalyticsTasks(TestCase):
|
|||||||
action,
|
action,
|
||||||
label,
|
label,
|
||||||
value).json()
|
value).json()
|
||||||
|
# then
|
||||||
self.assertFalse(json_response["hitParsingResult"][0]["valid"])
|
self.assertFalse(json_response["hitParsingResult"][0]["valid"])
|
||||||
self.assertEqual(json_response["hitParsingResult"][0]["parserMessage"][1]["description"], "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.")
|
self.assertEqual(
|
||||||
|
json_response["hitParsingResult"][0]["parserMessage"][1]["description"],
|
||||||
# [{'valid': False, 'parserMessage': [{'messageType': 'INFO', 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', 'messageCode': 'VALUE_MODIFIED'}, {'messageType': 'ERROR', 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", 'messageCode': 'VALUE_INVALID', 'parameter': 'tid'}], 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'}]
|
"The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details."
|
||||||
|
)
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
"all": ("authentication/css/admin.css",)
|
"all": ("allianceauth/authentication/css/admin.css",)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
@@ -542,7 +542,7 @@ class BaseOwnershipAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
"all": ("authentication/css/admin.css",)
|
"all": ("allianceauth/authentication/css/admin.css",)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import logging
|
|||||||
|
|
||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import ModelBackend
|
||||||
from django.contrib.auth.models import User, Permission
|
from django.contrib.auth.models import User, Permission
|
||||||
|
from django.contrib import messages
|
||||||
|
|
||||||
from .models import UserProfile, CharacterOwnership, OwnershipRecord
|
from .models import UserProfile, CharacterOwnership, OwnershipRecord
|
||||||
|
|
||||||
@@ -37,7 +38,13 @@ class StateBackend(ModelBackend):
|
|||||||
ownership = CharacterOwnership.objects.get(character__character_id=token.character_id)
|
ownership = CharacterOwnership.objects.get(character__character_id=token.character_id)
|
||||||
if ownership.owner_hash == token.character_owner_hash:
|
if ownership.owner_hash == token.character_owner_hash:
|
||||||
logger.debug(f'Authenticating {ownership.user} by ownership of character {token.character_name}')
|
logger.debug(f'Authenticating {ownership.user} by ownership of character {token.character_name}')
|
||||||
return ownership.user
|
if ownership.user.profile.main_character:
|
||||||
|
if ownership.user.profile.main_character.character_id == token.character_id:
|
||||||
|
return ownership.user
|
||||||
|
else: ## this is an alt, enforce main only.
|
||||||
|
if request:
|
||||||
|
messages.error("Unable to authenticate with this Character, Please log in with the main character associated with this account.")
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
logger.debug(f'{token.character_name} has changed ownership. Creating new user account.')
|
logger.debug(f'{token.character_name} has changed ownership. Creating new user account.')
|
||||||
ownership.delete()
|
ownership.delete()
|
||||||
@@ -57,13 +64,20 @@ class StateBackend(ModelBackend):
|
|||||||
if records.exists():
|
if records.exists():
|
||||||
# we've seen this character owner before. Re-attach to their old user account
|
# we've seen this character owner before. Re-attach to their old user account
|
||||||
user = records[0].user
|
user = records[0].user
|
||||||
|
if user.profile.main_character:
|
||||||
|
if ownership.user.profile.main_character.character_id != token.character_id:
|
||||||
|
## this is an alt, enforce main only due to trust issues in SSO.
|
||||||
|
if request:
|
||||||
|
messages.error("Unable to authenticate with this Character, Please log in with the main character associated with this account. Then add this character from the dashboard.")
|
||||||
|
return None
|
||||||
|
|
||||||
token.user = user
|
token.user = user
|
||||||
co = CharacterOwnership.objects.create_by_token(token)
|
co = CharacterOwnership.objects.create_by_token(token)
|
||||||
logger.debug(f'Authenticating {user} by matching owner hash record of character {co.character}')
|
logger.debug(f'Authenticating {user} by matching owner hash record of character {co.character}')
|
||||||
if not user.profile.main_character:
|
|
||||||
# set this as their main by default if they have none
|
# set this as their main by default as they have none
|
||||||
user.profile.main_character = co.character
|
user.profile.main_character = co.character
|
||||||
user.profile.save()
|
user.profile.save()
|
||||||
return user
|
return user
|
||||||
logger.debug(f'Unable to authenticate character {token.character_name}. Creating new user.')
|
logger.debug(f'Unable to authenticate character {token.character_name}. Creating new user.')
|
||||||
return self.create_user(token)
|
return self.create_user(token)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 4.0.10 on 2023-05-28 15:36
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentication", "0020_userprofile_language_userprofile_night_mode"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="userprofile",
|
||||||
|
name="language",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
choices=[
|
||||||
|
("en", "English"),
|
||||||
|
("de", "German"),
|
||||||
|
("es", "Spanish"),
|
||||||
|
("zh-hans", "Chinese Simplified"),
|
||||||
|
("ru", "Russian"),
|
||||||
|
("ko", "Korean"),
|
||||||
|
("fr", "French"),
|
||||||
|
("ja", "Japanese"),
|
||||||
|
("it", "Italian"),
|
||||||
|
("uk", "Ukrainian"),
|
||||||
|
],
|
||||||
|
default="",
|
||||||
|
max_length=10,
|
||||||
|
verbose_name="Language",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -18,13 +18,13 @@ class State(models.Model):
|
|||||||
priority = models.IntegerField(unique=True, help_text="Users get assigned the state with the highest priority available to them.")
|
priority = models.IntegerField(unique=True, help_text="Users get assigned the state with the highest priority available to them.")
|
||||||
|
|
||||||
member_characters = models.ManyToManyField(EveCharacter, blank=True,
|
member_characters = models.ManyToManyField(EveCharacter, blank=True,
|
||||||
help_text="Characters to which this state is available.")
|
help_text="Characters to which this state is available.")
|
||||||
member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True,
|
member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True,
|
||||||
help_text="Corporations to whose members this state is available.")
|
help_text="Corporations to whose members this state is available.")
|
||||||
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True,
|
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True,
|
||||||
help_text="Alliances to whose members this state is available.")
|
help_text="Alliances to whose members this state is available.")
|
||||||
member_factions = models.ManyToManyField(EveFactionInfo, blank=True,
|
member_factions = models.ManyToManyField(EveFactionInfo, blank=True,
|
||||||
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 = StateManager()
|
||||||
@@ -63,6 +63,22 @@ class UserProfile(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
default_permissions = ('change',)
|
default_permissions = ('change',)
|
||||||
|
|
||||||
|
class Language(models.TextChoices):
|
||||||
|
"""
|
||||||
|
Choices for UserProfile.language
|
||||||
|
"""
|
||||||
|
|
||||||
|
ENGLISH = 'en', _('English')
|
||||||
|
GERMAN = 'de', _('German')
|
||||||
|
SPANISH = 'es', _('Spanish')
|
||||||
|
CHINESE = 'zh-hans', _('Chinese Simplified')
|
||||||
|
RUSSIAN = 'ru', _('Russian')
|
||||||
|
KOREAN = 'ko', _('Korean')
|
||||||
|
FRENCH = 'fr', _('French')
|
||||||
|
JAPANESE = 'ja', _('Japanese')
|
||||||
|
ITALIAN = 'it', _('Italian')
|
||||||
|
UKRAINIAN = 'uk', _('Ukrainian')
|
||||||
|
|
||||||
user = models.OneToOneField(
|
user = models.OneToOneField(
|
||||||
User,
|
User,
|
||||||
related_name='profile',
|
related_name='profile',
|
||||||
@@ -76,20 +92,9 @@ class UserProfile(models.Model):
|
|||||||
State,
|
State,
|
||||||
on_delete=models.SET_DEFAULT,
|
on_delete=models.SET_DEFAULT,
|
||||||
default=get_guest_state_pk)
|
default=get_guest_state_pk)
|
||||||
LANGUAGE_CHOICES = [
|
|
||||||
('en', _('English')),
|
|
||||||
('de', _('German')),
|
|
||||||
('es', _('Spanish')),
|
|
||||||
('zh-hans', _('Chinese Simplified')),
|
|
||||||
('ru', _('Russian')),
|
|
||||||
('ko', _('Korean')),
|
|
||||||
('fr', _('French')),
|
|
||||||
('ja', _('Japanese')),
|
|
||||||
('it', _('Italian')),
|
|
||||||
]
|
|
||||||
language = models.CharField(
|
language = models.CharField(
|
||||||
_("Language"), max_length=10,
|
_("Language"), max_length=10,
|
||||||
choices=LANGUAGE_CHOICES,
|
choices=Language.choices,
|
||||||
blank=True,
|
blank=True,
|
||||||
default='')
|
default='')
|
||||||
night_mode = models.BooleanField(
|
night_mode = models.BooleanField(
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
CSS for allianceauth admin site
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* styling for profile pic */
|
||||||
|
.img-circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-user_profile_pic {
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tooltip */
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover::after {
|
||||||
|
background-color: rgb(255 255 204);
|
||||||
|
border: 1px rgb(128 128 128) solid;
|
||||||
|
color: rgb(0 0 0);
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
left: 1em;
|
||||||
|
min-width: 200px;
|
||||||
|
padding: 8px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1.1em;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
CSS for allianceauth admin site
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* styling for profile pic */
|
|
||||||
.img-circle {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.column-user_profile_pic {
|
|
||||||
width: 1px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tooltip */
|
|
||||||
.tooltip {
|
|
||||||
position: relative ;
|
|
||||||
}
|
|
||||||
.tooltip:hover::after {
|
|
||||||
content: attr(data-tooltip) ;
|
|
||||||
position: absolute ;
|
|
||||||
top: 1.1em ;
|
|
||||||
left: 1em ;
|
|
||||||
min-width: 200px ;
|
|
||||||
border: 1px #808080 solid ;
|
|
||||||
padding: 8px ;
|
|
||||||
color: black ;
|
|
||||||
background-color: rgb(255, 255, 204) ;
|
|
||||||
z-index: 1 ;
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ from typing import List, Optional
|
|||||||
from pytz import utc
|
from pytz import utc
|
||||||
from redis import Redis, RedisError
|
from redis import Redis, RedisError
|
||||||
|
|
||||||
from django_redis import get_redis_connection
|
from allianceauth.utils.cache import get_redis_client
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ class EventSeries:
|
|||||||
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
|
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
|
||||||
|
|
||||||
def __init__(self, key_id: str, redis: Redis = None) -> None:
|
def __init__(self, key_id: str, redis: Redis = None) -> None:
|
||||||
self._redis = get_redis_connection("default") if not redis else redis
|
self._redis = get_redis_client() if not redis else redis
|
||||||
try:
|
try:
|
||||||
if not self._redis.ping():
|
if not self._redis.ping():
|
||||||
raise RuntimeError()
|
raise RuntimeError()
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ MODULE_PATH = "allianceauth.authentication.task_statistics.event_series"
|
|||||||
class TestEventSeries(TestCase):
|
class TestEventSeries(TestCase):
|
||||||
def test_should_abort_without_redis_client(self):
|
def test_should_abort_without_redis_client(self):
|
||||||
# when
|
# when
|
||||||
with patch(MODULE_PATH + ".get_redis_connection") as mock:
|
with patch(MODULE_PATH + ".get_redis_client") as mock:
|
||||||
mock.return_value = None
|
mock.return_value = None
|
||||||
events = EventSeries("dummy")
|
events = EventSeries("dummy")
|
||||||
# then
|
# then
|
||||||
@@ -27,8 +27,8 @@ class TestEventSeries(TestCase):
|
|||||||
|
|
||||||
def test_should_disable_itself_if_redis_not_available_1(self):
|
def test_should_disable_itself_if_redis_not_available_1(self):
|
||||||
# when
|
# when
|
||||||
with patch(MODULE_PATH + ".get_redis_connection") as mock_get_redis_connection:
|
with patch(MODULE_PATH + ".get_redis_client") as mock_get_master_client:
|
||||||
mock_get_redis_connection.return_value.ping.side_effect = RedisError
|
mock_get_master_client.return_value.ping.side_effect = RedisError
|
||||||
events = EventSeries("dummy")
|
events = EventSeries("dummy")
|
||||||
# then
|
# then
|
||||||
self.assertIsInstance(events._redis, _RedisStub)
|
self.assertIsInstance(events._redis, _RedisStub)
|
||||||
@@ -36,8 +36,8 @@ class TestEventSeries(TestCase):
|
|||||||
|
|
||||||
def test_should_disable_itself_if_redis_not_available_2(self):
|
def test_should_disable_itself_if_redis_not_available_2(self):
|
||||||
# when
|
# when
|
||||||
with patch(MODULE_PATH + ".get_redis_connection") as mock_get_redis_connection:
|
with patch(MODULE_PATH + ".get_redis_client") as mock_get_master_client:
|
||||||
mock_get_redis_connection.return_value.ping.return_value = False
|
mock_get_master_client.return_value.ping.return_value = False
|
||||||
events = EventSeries("dummy")
|
events = EventSeries("dummy")
|
||||||
# then
|
# then
|
||||||
self.assertIsInstance(events._redis, _RedisStub)
|
self.assertIsInstance(events._redis, _RedisStub)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Dashboard" %}{% endblock %}
|
{% block page_title %}{% translate "Dashboard" %}{% endblock %}
|
||||||
@@ -15,9 +14,9 @@
|
|||||||
<div class="panel panel-primary" style="height:100%">
|
<div class="panel panel-primary" style="height:100%">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">
|
<h3 class="panel-title">
|
||||||
{% blocktrans with state=request.user.profile.state %}
|
{% blocktranslate with state=request.user.profile.state %}
|
||||||
Main Character (State: {{ state }})
|
Main Character (State: {{ state }})
|
||||||
{% endblocktrans %}
|
{% endblocktranslate %}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
@@ -28,7 +27,7 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<img class="ra-avatar"src="{{ main.portrait_url_128 }}">
|
<img class="ra-avatar" src="{{ main.portrait_url_128 }}" alt="{{ main.character_name }}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -40,7 +39,7 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<img class="ra-avatar"src="{{ main.corporation_logo_url_128 }}">
|
<img class="ra-avatar" src="{{ main.corporation_logo_url_128 }}" alt="{{ main.corporation_name }}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -53,7 +52,7 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<img class="ra-avatar"src="{{ main.alliance_logo_url_128 }}">
|
<img class="ra-avatar" src="{{ main.alliance_logo_url_128 }}" alt="{{ main.alliance_name }}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -64,7 +63,7 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<img class="ra-avatar"src="{{ main.faction_logo_url_128 }}">
|
<img class="ra-avatar" src="{{ main.faction_logo_url_128 }}" alt="{{ main.faction_name }}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -76,13 +75,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="table visible-xs-block">
|
<div class="table visible-xs-block">
|
||||||
<p>
|
<p>
|
||||||
<img class="ra-avatar" src="{{ main.portrait_url_64 }}">
|
<img class="ra-avatar" src="{{ main.portrait_url_64 }}" alt="{{ main.corporation_name }}">
|
||||||
<img class="ra-avatar" src="{{ main.corporation_logo_url_64 }}">
|
<img class="ra-avatar" src="{{ main.corporation_logo_url_64 }}" alt="{{ main.corporation_name }}">
|
||||||
{% if main.alliance_id %}
|
{% if main.alliance_id %}
|
||||||
<img class="ra-avatar" src="{{ main.alliance_logo_url_64 }}">
|
<img class="ra-avatar" src="{{ main.alliance_logo_url_64 }}" alt="{{ main.alliance_name }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if main.faction_id %}
|
{% if main.faction_id %}
|
||||||
<img class="ra-avatar" src="{{ main.faction_logo_url_64 }}">
|
<img class="ra-avatar" src="{{ main.faction_logo_url_64 }}" alt="{{ main.faction_name }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -104,13 +103,17 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 button-wrapper">
|
<div class="col-sm-6">
|
||||||
<a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info"
|
<p>
|
||||||
title="Add Character">{% translate 'Add Character' %}</a>
|
<a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info"
|
||||||
|
title="Add Character">{% translate 'Add Character' %}</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 button-wrapper">
|
<div class="col-sm-6">
|
||||||
<a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info"
|
<p>
|
||||||
title="Change Main Character">{% translate "Change Main" %}</a>
|
<a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info"
|
||||||
|
title="Change Main Character">{% translate "Change Main" %}</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,7 +125,7 @@
|
|||||||
<h3 class="panel-title">{% translate "Group Memberships" %}</h3>
|
<h3 class="panel-title">{% translate "Group Memberships" %}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div style="height: 240px;overflow:-moz-scrollbars-vertical;overflow-y:auto;">
|
<div style="height: 240px;overflow-y:auto;">
|
||||||
<table class="table table-aa">
|
<table class="table table-aa">
|
||||||
{% for group in groups %}
|
{% for group in groups %}
|
||||||
<tr>
|
<tr>
|
||||||
@@ -155,11 +158,12 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for char in characters %}
|
{% for char in characters %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
<td class="text-center">
|
||||||
|
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}" alt="{{ char.character_name }}">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ char.character_name }}</td>
|
<td class="text-center">{{ char.character_name }}</td>
|
||||||
<td class="text-center">{{ char.corporation_name }}</td>
|
<td class="text-center">{{ char.corporation_name }}</td>
|
||||||
<td class="text-center">{{ char.alliance_name }}</td>
|
<td class="text-center">{{ char.alliance_name|default:"" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -169,7 +173,7 @@
|
|||||||
{% for char in characters %}
|
{% for char in characters %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center" style="vertical-align: middle">
|
<td class="text-center" style="vertical-align: middle">
|
||||||
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}" alt="{{ char.character_name }}">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center" style="vertical-align: middle; width: 100%">
|
<td class="text-center" style="vertical-align: middle; width: 100%">
|
||||||
<strong>{{ char.character_name }}</strong><br>
|
<strong>{{ char.character_name }}</strong><br>
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
{% extends "allianceauth/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block page_title %}{% translate "Dashboard" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1 class="page-header text-center">{% translate "Token Management" %}</h1>
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<table class="table table-aa" id="table_tokens" style="width:100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% translate "Scopes" %}</th>
|
||||||
|
<th class="text-right">{% translate "Actions" %}</th>
|
||||||
|
<th>{% translate "Character" %}</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for t in tokens %}
|
||||||
|
<tr>
|
||||||
|
<td styl="white-space:initial;">{% for s in t.scopes.all %}<span class="label label-default">{{s.name}}</span> {% endfor %}</td>
|
||||||
|
<td nowrap class="text-right"><a href="{% url 'authentication:token_delete' t.id %}" class="btn btn-danger"><i class="fas fa-trash"></i></a> <a href="{% url 'authentication:token_refresh' t.id %}" class="btn btn-success"><i class="fas fa-sync-alt"></i></a></td>
|
||||||
|
<td>{{t.character_name}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://community.eveonline.com/support/third-party-applications/ where possible."|urlize %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_javascript %}
|
||||||
|
{% include 'bundles/datatables-js.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
{% include 'bundles/datatables-css.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_script %}
|
||||||
|
$(document).ready(function(){
|
||||||
|
let grp = 2;
|
||||||
|
var table = $('#table_tokens').DataTable({
|
||||||
|
"columnDefs": [{ orderable: false, targets: [0,1] },{ "visible": false, "targets": grp }],
|
||||||
|
"order": [[grp, 'asc']],
|
||||||
|
"drawCallback": function (settings) {
|
||||||
|
var api = this.api();
|
||||||
|
var rows = api.rows({ page: 'current' }).nodes();
|
||||||
|
var last = null;
|
||||||
|
api.column(grp, { page: 'current' })
|
||||||
|
.data()
|
||||||
|
.each(function (group, i) {
|
||||||
|
if (last !== group) {
|
||||||
|
$(rows).eq(i).before('<tr class="info"><td colspan="3">' + group + '</td></tr>');
|
||||||
|
last = group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"stateSave": true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{% endblock %}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
@@ -7,7 +8,7 @@
|
|||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
<meta property="og:title" content="{{ SITE_NAME }}">
|
<meta property="og:title" content="{{ SITE_NAME }}">
|
||||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'icons/apple-touch-icon.png' %}">
|
<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 property="og:description" content="Alliance Auth - An auth system for EVE Online to help in-game organizations manage online service access.">
|
||||||
|
|
||||||
{% include 'allianceauth/icons.html' %}
|
{% include 'allianceauth/icons.html' %}
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background: url('{% static 'authentication/img/background.jpg' %}') no-repeat center center fixed;
|
background: url('{% static 'allianceauth/authentication/img/background.jpg' %}') no-repeat center center fixed;
|
||||||
-webkit-background-size: cover;
|
-webkit-background-size: cover;
|
||||||
-moz-background-size: cover;
|
-moz-background-size: cover;
|
||||||
-o-background-size: cover;
|
-o-background-size: cover;
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
.panel-transparent {
|
.panel-transparent {
|
||||||
background: rgba(48, 48, 48, 0.7);
|
background: rgba(48, 48, 48, 0.7);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
padding-bottom: 21px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-body {
|
.panel-body {
|
||||||
@@ -47,7 +49,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container" style="margin-top:150px">
|
<div class="container" style="margin-top:150px;">
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
{% get_language_info_list for LANGUAGES as languages %}
|
{% get_language_info_list for LANGUAGES as languages %}
|
||||||
{% for language in languages %}
|
{% for language in languages %}
|
||||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||||
{{ language.name_local }} ({{ language.code }})
|
{{ language.name_local|capfirst }} ({{ language.code }})
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -7,6 +7,6 @@
|
|||||||
|
|
||||||
{% block middle_box_content %}
|
{% block middle_box_content %}
|
||||||
<a href="{% url 'auth_sso_login' %}{% if request.GET.next %}?next={{request.GET.next}}{%endif%}">
|
<a href="{% url 'auth_sso_login' %}{% if request.GET.next %}?next={{request.GET.next}}{%endif%}">
|
||||||
<img class="img-responsive center-block" src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>
|
<img class="img-responsive center-block" src="{% static 'allianceauth/authentication/img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" alt="{% translate 'Login with Eve SSO' %}">
|
||||||
</a>
|
</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
{% extends 'public/base.html' %}
|
{% extends 'public/base.html' %}
|
||||||
{% load static %}
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-md-4 col-md-offset-4">
|
<div class="col-md-4 col-md-offset-4">
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
@@ -7,6 +9,7 @@
|
|||||||
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
|
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="panel panel-default panel-transparent">
|
<div class="panel panel-default panel-transparent">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@@ -14,10 +17,25 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'public/lang_select.html' %}
|
{% include 'public/lang_select.html' %}
|
||||||
|
|
||||||
|
<p class="text-center" style="margin-top: 2rem;">
|
||||||
|
{% translate "For information on SSO, ESI and security read the CCP Dev Blog" %}<br>
|
||||||
|
<a href="https://www.eveonline.com/article/introducing-esi" target="_blank" rel="noopener noreferrer">
|
||||||
|
{% translate "Introducing ESI - A New API For Eve Online" %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="text-center">
|
||||||
|
<a href="https://community.eveonline.com/support/third-party-applications/" target="_blank" rel="noopener noreferrer">
|
||||||
|
{% translate "Manage ESI Applications" %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_include %}
|
{% block extra_include %}
|
||||||
{% include 'bundles/bootstrap-js.html' %}
|
{% include 'bundles/bootstrap-js.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{% extends 'public/base.html' %}
|
{% extends 'public/base.html' %}
|
||||||
|
|
||||||
{% load static %}
|
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
{% load i18n %}{% autoescape off %}
|
|
||||||
{% blocktrans trimmed %}You're receiving this email because you requested a password reset for your
|
|
||||||
user account.{% endblocktrans %}
|
|
||||||
|
|
||||||
{% translate "Please go to the following page and choose a new password:" %}
|
|
||||||
{% block reset_link %}
|
|
||||||
{{domain}}{% url 'password_reset_confirm' uidb64=uid token=token %}
|
|
||||||
{% endblock %}
|
|
||||||
{% translate "Your username, in case you've forgotten:" %} {{ user.get_username }}
|
|
||||||
|
|
||||||
{% translate "Thanks for using our site!" %}
|
|
||||||
|
|
||||||
{% blocktrans %}Your IT Team{% endblocktrans %}
|
|
||||||
|
|
||||||
{% endautoescape %}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{% extends 'public/middle_box.html' %}
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load static %}
|
|
||||||
{% block page_title %}{% translate "Register" %}{% endblock %}
|
|
||||||
{% block middle_box_content %}
|
|
||||||
<form class="form-signin" role="form" action="" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ form|bootstrap }}
|
|
||||||
<br>
|
|
||||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Submit" %}</button>
|
|
||||||
<br>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -116,10 +116,17 @@ class TestAuthenticate(TestCase):
|
|||||||
user = StateBackend().authenticate(token=t)
|
user = StateBackend().authenticate(token=t)
|
||||||
self.assertEqual(user, self.user)
|
self.assertEqual(user, self.user)
|
||||||
|
|
||||||
|
""" Alt Login disabled
|
||||||
def test_authenticate_alt_character(self):
|
def test_authenticate_alt_character(self):
|
||||||
t = Token(character_id=self.alt_character.character_id, character_owner_hash='2')
|
t = Token(character_id=self.alt_character.character_id, character_owner_hash='2')
|
||||||
user = StateBackend().authenticate(token=t)
|
user = StateBackend().authenticate(token=t)
|
||||||
self.assertEqual(user, self.user)
|
self.assertEqual(user, self.user)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_authenticate_alt_character_fail(self):
|
||||||
|
t = Token(character_id=self.alt_character.character_id, character_owner_hash='2')
|
||||||
|
user = StateBackend().authenticate(token=t)
|
||||||
|
self.assertEqual(user, None)
|
||||||
|
|
||||||
def test_authenticate_unclaimed_character(self):
|
def test_authenticate_unclaimed_character(self):
|
||||||
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='3')
|
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='3')
|
||||||
@@ -128,6 +135,7 @@ class TestAuthenticate(TestCase):
|
|||||||
self.assertEqual(user.username, 'Unclaimed_Character')
|
self.assertEqual(user.username, 'Unclaimed_Character')
|
||||||
self.assertEqual(user.profile.main_character, self.unclaimed_character)
|
self.assertEqual(user.profile.main_character, self.unclaimed_character)
|
||||||
|
|
||||||
|
""" Alt Login disabled
|
||||||
def test_authenticate_character_record(self):
|
def test_authenticate_character_record(self):
|
||||||
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='4')
|
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='4')
|
||||||
OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
|
OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
|
||||||
@@ -135,6 +143,15 @@ class TestAuthenticate(TestCase):
|
|||||||
self.assertEqual(user, self.old_user)
|
self.assertEqual(user, self.old_user)
|
||||||
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
|
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
|
||||||
self.assertTrue(user.profile.main_character)
|
self.assertTrue(user.profile.main_character)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_authenticate_character_record_fails(self):
|
||||||
|
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='4')
|
||||||
|
OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
|
||||||
|
user = StateBackend().authenticate(token=t)
|
||||||
|
self.assertEqual(user, self.old_user)
|
||||||
|
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
|
||||||
|
self.assertTrue(user.profile.main_character)
|
||||||
|
|
||||||
def test_iterate_username(self):
|
def test_iterate_username(self):
|
||||||
t = Token(character_id=self.unclaimed_character.character_id,
|
t = Token(character_id=self.unclaimed_character.character_id,
|
||||||
|
|||||||
@@ -22,5 +22,20 @@ urlpatterns = [
|
|||||||
views.add_character,
|
views.add_character,
|
||||||
name='add_character'
|
name='add_character'
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
'account/tokens/manage/',
|
||||||
|
views.token_management,
|
||||||
|
name='token_management'
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
'account/tokens/delete/<int:token_id>',
|
||||||
|
views.token_delete,
|
||||||
|
name='token_delete'
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
'account/tokens/refresh/<int:token_id>',
|
||||||
|
views.token_refresh,
|
||||||
|
name='token_refresh'
|
||||||
|
),
|
||||||
path('dashboard/', views.dashboard, name='dashboard'),
|
path('dashboard/', views.dashboard, name='dashboard'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -61,6 +61,44 @@ def dashboard(request):
|
|||||||
}
|
}
|
||||||
return render(request, 'authentication/dashboard.html', context)
|
return render(request, 'authentication/dashboard.html', context)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def token_management(request):
|
||||||
|
tokens = request.user.token_set.all()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'tokens': tokens
|
||||||
|
}
|
||||||
|
return render(request, 'authentication/tokens.html', context)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def token_delete(request, token_id=None):
|
||||||
|
try:
|
||||||
|
token = Token.objects.get(id=token_id)
|
||||||
|
if request.user == token.user:
|
||||||
|
token.delete()
|
||||||
|
messages.success(request, "Token Deleted.")
|
||||||
|
else:
|
||||||
|
messages.error(request, "This token does not belong to you.")
|
||||||
|
except Token.DoesNotExist:
|
||||||
|
messages.warning(request, "Token does not exist")
|
||||||
|
return redirect('authentication:token_management')
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def token_refresh(request, token_id=None):
|
||||||
|
try:
|
||||||
|
token = Token.objects.get(id=token_id)
|
||||||
|
if request.user == token.user:
|
||||||
|
try:
|
||||||
|
token.refresh()
|
||||||
|
messages.success(request, "Token refreshed.")
|
||||||
|
except Exception as e:
|
||||||
|
messages.warning(request, f"Failed to refresh token. {e}")
|
||||||
|
else:
|
||||||
|
messages.error(request, "This token does not belong to you.")
|
||||||
|
except Token.DoesNotExist:
|
||||||
|
messages.warning(request, "Token does not exist")
|
||||||
|
return redirect('authentication:token_management')
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
|
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ from .views import NightModeRedirectView
|
|||||||
def auth_settings(request):
|
def auth_settings(request):
|
||||||
return {
|
return {
|
||||||
'SITE_NAME': settings.SITE_NAME,
|
'SITE_NAME': settings.SITE_NAME,
|
||||||
|
'SITE_URL': settings.SITE_URL,
|
||||||
'NIGHT_MODE': NightModeRedirectView.night_mode_state(request),
|
'NIGHT_MODE': NightModeRedirectView.night_mode_state(request),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center col-lg-6{% if corpstats.corp.alliance %}{% else %}col-lg-offset-3{% endif %}">
|
<td class="text-center col-lg-6{% if corpstats.corp.alliance %}{% else %}col-lg-offset-3{% endif %}">
|
||||||
<img class="ra-avatar" src="{{ corpstats.corp.logo_url_64 }}">
|
<img class="ra-avatar" src="{{ corpstats.corp.logo_url_64 }}" alt="{{ corpstats.corp.corporation_name }}">
|
||||||
</td>
|
</td>
|
||||||
{% if corpstats.corp.alliance %}
|
{% if corpstats.corp.alliance %}
|
||||||
<td class="text-center col-lg-6">
|
<td class="text-center col-lg-6">
|
||||||
<img class="ra-avatar" src="{{ corpstats.corp.alliance.logo_url_64 }}">
|
<img class="ra-avatar" src="{{ corpstats.corp.alliance.logo_url_64 }}" alt="{{ corpstats.corp.alliance.alliance_name }}">
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="text-center" style="vertical-align:middle">
|
<td class="text-center" style="vertical-align:middle">
|
||||||
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||||
<img src="{{ main.main.portrait_url_64 }}" class="img-circle">
|
<img src="{{ main.main.portrait_url_64 }}" class="img-circle" alt="{{ main.main }}">
|
||||||
<div class="caption text-center">
|
<div class="caption text-center">
|
||||||
{{ main.main }}
|
{{ main.main }}
|
||||||
</div>
|
</div>
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="text-center" style="width:5%">
|
<td class="text-center" style="width:5%">
|
||||||
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||||
<img src="{{ alt.portrait_url_32 }}" class="img-circle">
|
<img src="{{ alt.portrait_url_32 }}" class="img-circle" alt="{{ alt.character_name }}">
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center" style="width:30%">{{ alt.character_name }}</td>
|
<td class="text-center" style="width:30%">{{ alt.character_name }}</td>
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for member in members %}
|
{% for member in members %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member }}"></td>
|
||||||
<td class="text-center">{{ member }}</td>
|
<td class="text-center">{{ member }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a>
|
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for member in unregistered %}
|
{% for member in unregistered %}
|
||||||
<tr class="danger">
|
<tr class="danger">
|
||||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td>
|
||||||
<td class="text-center">{{ member.character_name }}</td>
|
<td class="text-center">{{ member.character_name }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a>
|
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a>
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for member in unregistered %}
|
{% for member in unregistered %}
|
||||||
<tr class="danger">
|
<tr class="danger">
|
||||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td>
|
||||||
<td class="text-center">{{ member.character_name }}</td>
|
<td class="text-center">{{ member.character_name }}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">
|
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for result in results %}
|
{% for result in results %}
|
||||||
<tr {% if not result.1.registered %}class="danger"{% endif %}>
|
<tr {% if not result.1.registered %}class="danger"{% endif %}>
|
||||||
<td class="text-center"><img src="{{ result.1.portrait_url }}" class="img-circle"></td>
|
<td class="text-center"><img src="{{ result.1.portrait_url }}" class="img-circle" alt="{{ result.1.character_name }}"></td>
|
||||||
<td class="text-center">{{ result.1.character_name }}</td>
|
<td class="text-center">{{ result.1.character_name }}</td>
|
||||||
<td class="text-center">{{ result.0.corp.corporation_name }}</td>
|
<td class="text-center">{{ result.0.corp.corporation_name }}</td>
|
||||||
<td class="text-center"><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a></td>
|
<td class="text-center"><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a></td>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.0.7 on 2022-08-14 16:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('eveonline', '0016_character_names_are_not_unique'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='eveallianceinfo',
|
||||||
|
name='alliance_name',
|
||||||
|
field=models.CharField(max_length=254, db_index=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='evecorporationinfo',
|
||||||
|
name='corporation_name',
|
||||||
|
field=models.CharField(max_length=254, db_index=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -71,7 +71,7 @@ class EveAllianceInfo(models.Model):
|
|||||||
"""An alliance in Eve Online."""
|
"""An alliance in Eve Online."""
|
||||||
|
|
||||||
alliance_id = models.PositiveIntegerField(unique=True)
|
alliance_id = models.PositiveIntegerField(unique=True)
|
||||||
alliance_name = models.CharField(max_length=254, unique=True)
|
alliance_name = models.CharField(max_length=254, db_index=True)
|
||||||
alliance_ticker = models.CharField(max_length=254)
|
alliance_ticker = models.CharField(max_length=254)
|
||||||
executor_corp_id = models.PositiveIntegerField()
|
executor_corp_id = models.PositiveIntegerField()
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ class EveCorporationInfo(models.Model):
|
|||||||
"""A corporation in Eve Online."""
|
"""A corporation in Eve Online."""
|
||||||
|
|
||||||
corporation_id = models.PositiveIntegerField(unique=True)
|
corporation_id = models.PositiveIntegerField(unique=True)
|
||||||
corporation_name = models.CharField(max_length=254, unique=True)
|
corporation_name = models.CharField(max_length=254, db_index=True)
|
||||||
corporation_ticker = models.CharField(max_length=254)
|
corporation_ticker = models.CharField(max_length=254)
|
||||||
member_count = models.IntegerField()
|
member_count = models.IntegerField()
|
||||||
ceo_id = models.PositiveIntegerField(blank=True, null=True, default=None)
|
ceo_id = models.PositiveIntegerField(blank=True, null=True, default=None)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from django.conf import settings
|
|||||||
from esi.clients import esi_client_factory
|
from esi.clients import esi_client_factory
|
||||||
|
|
||||||
from allianceauth import __version__
|
from allianceauth import __version__
|
||||||
|
from allianceauth.utils.django import StartupCommand
|
||||||
|
|
||||||
|
|
||||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||||
@@ -175,15 +176,16 @@ class EveProvider:
|
|||||||
|
|
||||||
class EveSwaggerProvider(EveProvider):
|
class EveSwaggerProvider(EveProvider):
|
||||||
def __init__(self, token=None, adapter=None):
|
def __init__(self, token=None, adapter=None):
|
||||||
if settings.DEBUG:
|
if settings.DEBUG or StartupCommand().is_management_command:
|
||||||
self._client = None
|
self._client = None
|
||||||
logger.info(
|
logger.info('ESI client will be loaded on-demand')
|
||||||
'DEBUG mode detected: ESI client will be loaded on-demand.'
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
|
logger.info('Loading ESI client')
|
||||||
try:
|
try:
|
||||||
self._client = esi_client_factory(
|
self._client = esi_client_factory(
|
||||||
token=token, spec_file=SWAGGER_SPEC_PATH, app_info_text=("allianceauth v" + __version__)
|
token=token,
|
||||||
|
spec_file=SWAGGER_SPEC_PATH,
|
||||||
|
app_info_text=f"allianceauth v{__version__}"
|
||||||
)
|
)
|
||||||
except (HTTPError, RefResolutionError):
|
except (HTTPError, RefResolutionError):
|
||||||
logger.exception(
|
logger.exception(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="panel-heading">{{ character_name }}</div>
|
<div class="panel-heading">{{ character_name }}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="col-lg-2 col-sm-2">
|
<div class="col-lg-2 col-sm-2">
|
||||||
<img class="ra-avatar img-responsive" src="{{ character_portrait_url }}">
|
<img class="ra-avatar img-responsive" src="{{ character_portrait_url }}" alt="{{ character_name }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-10 col-sm-2">
|
<div class="col-lg-10 col-sm-2">
|
||||||
<div class="alert alert-danger" role="alert">{% translate "Character not registered!" %}</div>
|
<div class="alert alert-danger" role="alert">{% translate "Character not registered!" %}</div>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Create Fatlink" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Create Fatlink" %}{% endblock page_title %}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block page_title %}{% translate "Fatlink view" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Fatlink view" %}{% endblock page_title %}
|
||||||
|
|
||||||
@@ -32,7 +30,7 @@
|
|||||||
<td class="text-center">{{ fat.user }}</td>
|
<td class="text-center">{{ fat.user }}</td>
|
||||||
<td class="text-center">{{ fat.character.character_name }}</td>
|
<td class="text-center">{{ fat.character.character_name }}</td>
|
||||||
{% if fat.station != "No Station" %}
|
{% if fat.station != "No Station" %}
|
||||||
<td class="text-center">{% blocktrans %}Docked in {% endblocktrans %}{{ fat.system }}</td>
|
<td class="text-center">{% blocktranslate %}Docked in {% endblocktranslate %}{{ fat.system }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="text-center">{{ fat.system }}</td>
|
<td class="text-center">{{ fat.system }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Personal fatlink statistics" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Personal fatlink statistics" %}{% endblock page_title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ month }}, {{ year }}{% endblocktrans %}
|
<h1 class="page-header text-center">{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
|
||||||
{% if char_id %}
|
{% if char_id %}
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a href="{% url 'fatlink:user_statistics_month' char_id previous_month|date:'Y' previous_month|date:'m' %}" class="btn btn-info">{% translate "Previous month" %}</a>
|
<a href="{% url 'fatlink:user_statistics_month' char_id previous_month|date:'Y' previous_month|date:'m' %}" class="btn btn-info">{% translate "Previous month" %}</a>
|
||||||
@@ -16,11 +14,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</h1>
|
</h1>
|
||||||
<h2>
|
<h2>
|
||||||
{% blocktrans count links=n_fats trimmed %}
|
{% blocktranslate count links=n_fats trimmed %}
|
||||||
{{ user }} has collected one link this month.
|
{{ user }} has collected one link this month.
|
||||||
{% plural %}
|
{% plural %}
|
||||||
{{ user }} has collected {{ links }} links this month.
|
{{ user }} has collected {{ links }} links this month.
|
||||||
{% endblocktrans %}
|
{% endblocktranslate %}
|
||||||
</h2>
|
</h2>
|
||||||
<table class="table table-responsive">
|
<table class="table table-responsive">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -36,11 +34,11 @@
|
|||||||
</table>
|
</table>
|
||||||
{% if created_fats %}
|
{% if created_fats %}
|
||||||
<h2>
|
<h2>
|
||||||
{% blocktrans count links=n_created_fats trimmed %}
|
{% blocktranslate count links=n_created_fats trimmed %}
|
||||||
{{ user }} has created one link this month.
|
{{ user }} has created one link this month.
|
||||||
{% plural %}
|
{% plural %}
|
||||||
{{ user }} has created {{ links }} links this month.
|
{{ user }} has created {{ links }} links this month.
|
||||||
{% endblocktrans %}
|
{% endblocktranslate %}
|
||||||
</h2>
|
</h2>
|
||||||
{% if created_fats %}
|
{% if created_fats %}
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Personal fatlink statistics" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Personal fatlink statistics" %}{% endblock page_title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ year }}{% endblocktrans %}
|
<h1 class="page-header text-center">{% blocktranslate %}Participation data statistics for {{ year }}{% endblocktranslate %}
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a href="{% url 'fatlink:personal_statistics_year' previous_year %}" class="btn btn-info">{% translate "Previous year" %}</a>
|
<a href="{% url 'fatlink:personal_statistics_year' previous_year %}" class="btn btn-info">{% translate "Previous year" %}</a>
|
||||||
{% if next_year %}
|
{% if next_year %}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Fatlink Corp Statistics" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Fatlink Corp Statistics" %}{% endblock page_title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ month }}, {{ year }}{% endblocktrans %}
|
<h1 class="page-header text-center">{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a href="{% url 'fatlink:statistics_corp_month' corpid previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
|
<a href="{% url 'fatlink:statistics_corp_month' corpid previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
|
||||||
{% if next_month %}
|
{% if next_month %}
|
||||||
@@ -29,7 +27,7 @@
|
|||||||
{% for memberStat in fatStats %}
|
{% for memberStat in fatStats %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ memberStat.mainchar.portrait_url_32 }}" class="ra-avatar img-responsive">
|
<img src="{{ memberStat.mainchar.portrait_url_32 }}" class="ra-avatar img-responsive" alt="{{ memberStat.mainchar.character_name }}">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ memberStat.mainchar.character_name }}</td>
|
<td class="text-center">{{ memberStat.mainchar.character_name }}</td>
|
||||||
<td class="text-center">{{ memberStat.n_chars }}</td>
|
<td class="text-center">{{ memberStat.n_chars }}</td>
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Fatlink statistics" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Fatlink statistics" %}{% endblock page_title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ month }}, {{ year }}{% endblocktrans %}
|
<h1 class="page-header text-center">{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a href="{% url 'fatlink:statistics_month' previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
|
<a href="{% url 'fatlink:statistics_month' previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
|
||||||
{% if next_month %}
|
{% if next_month %}
|
||||||
@@ -30,9 +28,9 @@
|
|||||||
{% for corpStat in fatStats %}
|
{% for corpStat in fatStats %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ corpStat.corp.logo_url_32 }}" class="ra-avatar img-responsive">
|
<img src="{{ corpStat.corp.logo_url_32 }}" class="ra-avatar img-responsive" alt="{{ corpStat.corp.corporation_name }}">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center"><a href="{% url 'fatlink:statistics_corp' corpStat.corp.corporation_id %}">[{{ corpStat.corp.corporation_ticker }}]</td>
|
<td class="text-center"><a href="{% url 'fatlink:statistics_corp' corpStat.corp.corporation_id %}">[{{ corpStat.corp.corporation_ticker }}]</a></td>
|
||||||
<td class="text-center">{{ corpStat.corp.corporation_name }}</td>
|
<td class="text-center">{{ corpStat.corp.corporation_name }}</td>
|
||||||
<td class="text-center">{{ corpStat.corp.member_count }}</td>
|
<td class="text-center">{{ corpStat.corp.member_count }}</td>
|
||||||
<td class="text-center">{{ corpStat.n_fats }}</td>
|
<td class="text-center">{{ corpStat.n_fats }}</td>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Fatlink view" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Fatlink view" %}{% endblock page_title %}
|
||||||
@@ -35,7 +33,7 @@
|
|||||||
<td class="text-center">{{ fat.fatlink.fleet }}</td>
|
<td class="text-center">{{ fat.fatlink.fleet }}</td>
|
||||||
<td class="text-center">{{ fat.character.character_name }}</td>
|
<td class="text-center">{{ fat.character.character_name }}</td>
|
||||||
{% if fat.station != "No Station" %}
|
{% if fat.station != "No Station" %}
|
||||||
<td class="text-center">{% blocktrans %}Docked in {% endblocktrans %}{{ fat.system }}</td>
|
<td class="text-center">{% blocktranslate %}Docked in {% endblocktranslate %}{{ fat.system }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="text-center">{{ fat.system }}</td>
|
<td class="text-center">{{ fat.system }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -248,59 +248,82 @@ def fatlink_monthly_personal_statistics_view(request, year, month, char_id=None)
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@token_required(
|
@token_required(
|
||||||
scopes=['esi-location.read_location.v1', 'esi-location.read_ship_type.v1', 'esi-universe.read_structures.v1'])
|
scopes=[
|
||||||
|
'esi-location.read_location.v1',
|
||||||
|
'esi-location.read_ship_type.v1',
|
||||||
|
'esi-universe.read_structures.v1',
|
||||||
|
'esi-location.read_online.v1',
|
||||||
|
]
|
||||||
|
)
|
||||||
def click_fatlink_view(request, token, fat_hash=None):
|
def click_fatlink_view(request, token, fat_hash=None):
|
||||||
fatlink = get_object_or_404(Fatlink, hash=fat_hash)
|
c = token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
|
||||||
|
character = EveCharacter.objects.get_character_by_id(token.character_id)
|
||||||
|
character_online = c.Location.get_characters_character_id_online(
|
||||||
|
character_id=token.character_id
|
||||||
|
).result()
|
||||||
|
|
||||||
if (timezone.now() - fatlink.fatdatetime) < datetime.timedelta(seconds=(fatlink.duration * 60)):
|
if character_online["online"] is True:
|
||||||
|
fatlink = get_object_or_404(Fatlink, hash=fat_hash)
|
||||||
|
|
||||||
character = EveCharacter.objects.get_character_by_id(token.character_id)
|
if (timezone.now() - fatlink.fatdatetime) < datetime.timedelta(seconds=(fatlink.duration * 60)):
|
||||||
|
if character:
|
||||||
|
# get data
|
||||||
|
location = c.Location.get_characters_character_id_location(character_id=token.character_id).result()
|
||||||
|
ship = c.Location.get_characters_character_id_ship(character_id=token.character_id).result()
|
||||||
|
location['solar_system_name'] = \
|
||||||
|
c.Universe.get_universe_systems_system_id(system_id=location['solar_system_id']).result()['name']
|
||||||
|
|
||||||
if character:
|
if location['station_id']:
|
||||||
# get data
|
location['station_name'] = \
|
||||||
c = token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
|
c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()['name']
|
||||||
location = c.Location.get_characters_character_id_location(character_id=token.character_id).result()
|
elif location['structure_id']:
|
||||||
ship = c.Location.get_characters_character_id_ship(character_id=token.character_id).result()
|
location['station_name'] = \
|
||||||
location['solar_system_name'] = \
|
c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()[
|
||||||
c.Universe.get_universe_systems_system_id(system_id=location['solar_system_id']).result()['name']
|
'name']
|
||||||
if location['station_id']:
|
else:
|
||||||
location['station_name'] = \
|
location['station_name'] = "No Station"
|
||||||
c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()['name']
|
|
||||||
elif location['structure_id']:
|
ship['ship_type_name'] = provider.get_itemtype(ship['ship_type_id']).name
|
||||||
location['station_name'] = \
|
|
||||||
c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()[
|
fat = Fat()
|
||||||
'name']
|
fat.system = location['solar_system_name']
|
||||||
|
fat.station = location['station_name']
|
||||||
|
fat.shiptype = ship['ship_type_name']
|
||||||
|
fat.fatlink = fatlink
|
||||||
|
fat.character = character
|
||||||
|
fat.user = request.user
|
||||||
|
|
||||||
|
try:
|
||||||
|
fat.full_clean()
|
||||||
|
fat.save()
|
||||||
|
messages.success(request, _('Fleet participation registered.'))
|
||||||
|
except ValidationError as e:
|
||||||
|
err_messages = []
|
||||||
|
|
||||||
|
for errorname, message in e.message_dict.items():
|
||||||
|
err_messages.append(message[0])
|
||||||
|
|
||||||
|
messages.error(request, ' '.join(err_messages))
|
||||||
else:
|
else:
|
||||||
location['station_name'] = "No Station"
|
context = {
|
||||||
ship['ship_type_name'] = provider.get_itemtype(ship['ship_type_id']).name
|
'character_id': token.character_id,
|
||||||
|
'character_name': token.character_name,
|
||||||
|
'character_portrait_url': EveCharacter.generic_portrait_url(
|
||||||
|
token.character_id, 128
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
fat = Fat()
|
return render(request, 'fleetactivitytracking/characternotexisting.html', context=context)
|
||||||
fat.system = location['solar_system_name']
|
|
||||||
fat.station = location['station_name']
|
|
||||||
fat.shiptype = ship['ship_type_name']
|
|
||||||
fat.fatlink = fatlink
|
|
||||||
fat.character = character
|
|
||||||
fat.user = request.user
|
|
||||||
try:
|
|
||||||
fat.full_clean()
|
|
||||||
fat.save()
|
|
||||||
messages.success(request, _('Fleet participation registered.'))
|
|
||||||
except ValidationError as e:
|
|
||||||
err_messages = []
|
|
||||||
for errorname, message in e.message_dict.items():
|
|
||||||
err_messages.append(message[0])
|
|
||||||
messages.error(request, ' '.join(err_messages))
|
|
||||||
else:
|
else:
|
||||||
context = {
|
messages.error(request, _('FAT link has expired.'))
|
||||||
'character_id': token.character_id,
|
|
||||||
'character_name': token.character_name,
|
|
||||||
'character_portrait_url': EveCharacter.generic_portrait_url(
|
|
||||||
token.character_id, 128
|
|
||||||
),
|
|
||||||
}
|
|
||||||
return render(request, 'fleetactivitytracking/characternotexisting.html', context=context)
|
|
||||||
else:
|
else:
|
||||||
messages.error(request, _('FAT link has expired.'))
|
messages.warning(
|
||||||
|
request,
|
||||||
|
_(
|
||||||
|
f"Cannot register the fleet participation for {character.character_name}. The character needs to be online."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
return redirect('fatlink:view')
|
return redirect('fatlink:view')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{{ group }} {% translate "Audit Log" %}{% endblock page_title %}
|
{% block page_title %}{{ group }} {% translate "Audit Log" %}{% endblock page_title %}
|
||||||
@@ -25,13 +24,15 @@
|
|||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped" id="log-entries">
|
<table class="table table-striped" id="log-entries">
|
||||||
<thead>
|
<thead>
|
||||||
<th scope="col">{% translate "Date/Time" %}</th>
|
<tr>
|
||||||
<th scope="col">{% translate "Requestor" %}</th>
|
<th scope="col">{% translate "Date/Time" %}</th>
|
||||||
<th scope="col">{% translate "Character" %}</th>
|
<th scope="col">{% translate "Requestor" %}</th>
|
||||||
<th scope="col">{% translate "Corporation" %}</th>
|
<th scope="col">{% translate "Character" %}</th>
|
||||||
<th scope="col">{% translate "Type" %}</th>
|
<th scope="col">{% translate "Corporation" %}</th>
|
||||||
<th scope="col">{% translate "Action" %}</th>
|
<th scope="col">{% translate "Type" %}</th>
|
||||||
<th scope="col">{% translate "Actor" %}</th>
|
<th scope="col">{% translate "Action" %}</th>
|
||||||
|
<th scope="col">{% translate "Actor" %}</th>
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
{% block extra_javascript %}
|
{% block extra_javascript %}
|
||||||
{% include 'bundles/datatables-js.html' %}
|
{% include 'bundles/datatables-js.html' %}
|
||||||
{% include 'bundles/moment-js.html' with locale=True %}
|
{% include 'bundles/moment-js.html' with locale=True %}
|
||||||
<script type="application/javascript" src="{% static 'js/filterDropDown/filterDropDown.min.js' %}"></script>
|
{% include 'bundles/filterdropdown-js.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load evelinks %}
|
{% load evelinks %}
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@
|
|||||||
{% for member in members %}
|
{% for member in members %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ member.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;">
|
<img src="{{ member.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;" alt="{{ member.main_char.character_name }}">
|
||||||
{% if member.main_char %}
|
{% if member.main_char %}
|
||||||
<a href="{{ member.main_char|evewho_character_url }}" target="_blank">
|
<a href="{{ member.main_char|evewho_character_url }}" target="_blank">
|
||||||
{{ member.main_char.character_name }}
|
{{ member.main_char.character_name }}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Groups Membership" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Groups Membership" %}{% endblock page_title %}
|
||||||
@@ -61,7 +60,7 @@
|
|||||||
<i class="glyphicon glyphicon-list-alt"></i>
|
<i class="glyphicon glyphicon-list-alt"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a id="clipboard-copy" data-clipboard-text="{{ request.scheme }}://{{request.get_host}}{% url 'groupmanagement:request_add' group.id %}" class="btn btn-warning" title="{% translate "Copy Direct Join Link" %}">
|
<a id="clipboard-copy" data-clipboard-text="{{ SITE_URL }}{% url 'groupmanagement:request_add' group.id %}" class="btn btn-warning" title="{% translate "Copy Direct Join Link" %}">
|
||||||
<i class="glyphicon glyphicon-copy"></i>
|
<i class="glyphicon glyphicon-copy"></i>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Available Groups" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Available Groups" %}{% endblock page_title %}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load evelinks %}
|
{% load evelinks %}
|
||||||
|
|
||||||
@@ -64,7 +63,7 @@
|
|||||||
{% for acceptrequest in acceptrequests %}
|
{% for acceptrequest in acceptrequests %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ acceptrequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;">
|
<img src="{{ acceptrequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;" alt="{{ acceptrequest.main_char.character_name }}">
|
||||||
{% if acceptrequest.main_char %}
|
{% if acceptrequest.main_char %}
|
||||||
<a href="{{ acceptrequest.main_char|evewho_character_url }}" target="_blank">
|
<a href="{{ acceptrequest.main_char|evewho_character_url }}" target="_blank">
|
||||||
{{ acceptrequest.main_char.character_name }}
|
{{ acceptrequest.main_char.character_name }}
|
||||||
@@ -121,7 +120,7 @@
|
|||||||
{% for leaverequest in leaverequests %}
|
{% for leaverequest in leaverequests %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;">
|
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;" alt="{{ leaverequest.main_char.character_name }}">
|
||||||
{% if leaverequest.main_char %}
|
{% if leaverequest.main_char %}
|
||||||
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
||||||
{{ leaverequest.main_char.character_name }}
|
{{ leaverequest.main_char.character_name }}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load navactive %}
|
{% load navactive %}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ app_name = "groupmanagement"
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# groups
|
# groups
|
||||||
path("groups", views.groups_view, name="groups"),
|
path("groups/", views.groups_view, name="groups"),
|
||||||
path("group/request/join/<int:group_id>/", views.group_request_add, name="request_add"),
|
path("group/request/join/<int:group_id>/", views.group_request_add, name="request_add"),
|
||||||
path(
|
path(
|
||||||
"group/request/leave/<int:group_id>/", views.group_request_leave, name="request_leave"
|
"group/request/leave/<int:group_id>/", views.group_request_leave, name="request_leave"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Choose a Corp" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Choose a Corp" %}{% endblock page_title %}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Apply To" %} {{ corp.corporation_name }}{% endblock page_title %}
|
{% block page_title %}{% translate "Apply To" %} {{ corp.corporation_name }}{% endblock page_title %}
|
||||||
@@ -16,11 +15,11 @@
|
|||||||
<label class="control-label" for="id_{{ question.pk }}">{{ question.title }}</label>
|
<label class="control-label" for="id_{{ question.pk }}">{{ question.title }}</label>
|
||||||
<div class=" ">
|
<div class=" ">
|
||||||
{% if question.help_text %}
|
{% if question.help_text %}
|
||||||
<div cass="text-center">{{ question.help_text }}</div>
|
<div class="text-center">{{ question.help_text }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for choice in question.choices.all %}
|
{% for choice in question.choices.all %}
|
||||||
<input type={% if question.multi_select == False %}"radio"{% else %}"checkbox"{% endif %} name="{{ question.pk }}" id="id_{{ question.pk }}" value="{{ choice.choice_text }}">
|
<input type={% if question.multi_select == False %}"radio"{% else %}"checkbox"{% endif %} name="{{ question.pk }}" id="id_{{ question.pk }}_choice_{{ forloop.counter }}" value="{{ choice.choice_text }}">
|
||||||
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
|
<label for="id_{{ question.pk }}_choice_{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<textarea class="form-control" cols="30" id="id_{{ question.pk }}" name="{{ question.pk }}" rows="4"></textarea>
|
<textarea class="form-control" cols="30" id="id_{{ question.pk }}" name="{{ question.pk }}" rows="4"></textarea>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "HR Application Management" %}{% endblock page_title %}
|
{% block page_title %}{% translate "HR Application Management" %}{% endblock page_title %}
|
||||||
@@ -178,7 +177,7 @@
|
|||||||
<h4 class="modal-title" id="myModalLabel">{% translate "Application Search" %}</h4>
|
<h4 class="modal-title" id="myModalLabel">{% translate "Application Search" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-signin" role="form" action={% url 'hrapplications:search' %} method="POST">
|
<form class="form-signin" role="form" action="{% url 'hrapplications:search' %}" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ search_form|bootstrap }}
|
{{ search_form|bootstrap }}
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "HR Application Management" %}{% endblock page_title %}
|
{% block page_title %}{% translate "HR Application Management" %}{% endblock page_title %}
|
||||||
@@ -64,7 +63,7 @@
|
|||||||
<h4 class="modal-title" id="myModalLabel">{% translate "Application Search" %}</h4>
|
<h4 class="modal-title" id="myModalLabel">{% translate "Application Search" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-signin" role="form" action={% url 'hrapplications:search' %} method="POST">
|
<form class="form-signin" role="form" action="{% url 'hrapplications:search' %}" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ search_form|bootstrap }}
|
{{ search_form|bootstrap }}
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@
|
|||||||
{% for char in app.characters %}
|
{% for char in app.characters %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<img class="ra-avatar img-responsive img-circle" src="{{ char.portrait_url_32 }}">
|
<img class="ra-avatar img-responsive img-circle" src="{{ char.portrait_url_32 }}" alt="{{ char.character_name }}">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ char.character_name }}</td>
|
<td class="text-center">{{ char.character_name }}</td>
|
||||||
<td class="text-center">{{ char.corporation_name }}</td>
|
<td class="text-center">{{ char.corporation_name }}</td>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ def hr_application_create_view(request, form_id=None):
|
|||||||
app_form = get_object_or_404(ApplicationForm, id=form_id)
|
app_form = get_object_or_404(ApplicationForm, id=form_id)
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if Application.objects.filter(user=request.user).filter(form=app_form).exists():
|
if Application.objects.filter(user=request.user).filter(form=app_form).exists():
|
||||||
logger.warn(f"User {request.user} attempting to duplicate application to {app_form.corp}")
|
logger.warning(f"User {request.user} attempting to duplicate application to {app_form.corp}")
|
||||||
else:
|
else:
|
||||||
application = Application(user=request.user, form=app_form)
|
application = Application(user=request.user, form=app_form)
|
||||||
application.save()
|
application.save()
|
||||||
@@ -92,7 +92,7 @@ def hr_application_personal_view(request, app_id):
|
|||||||
}
|
}
|
||||||
return render(request, 'hrapplications/view.html', context=context)
|
return render(request, 'hrapplications/view.html', context=context)
|
||||||
else:
|
else:
|
||||||
logger.warn(f"User {request.user} not authorized to view {app}")
|
logger.warning(f"User {request.user} not authorized to view {app}")
|
||||||
return redirect('hrapplications:personal_view')
|
return redirect('hrapplications:personal_view')
|
||||||
|
|
||||||
|
|
||||||
@@ -105,9 +105,9 @@ def hr_application_personal_removal(request, app_id):
|
|||||||
logger.info(f"User {request.user} deleting {app}")
|
logger.info(f"User {request.user} deleting {app}")
|
||||||
app.delete()
|
app.delete()
|
||||||
else:
|
else:
|
||||||
logger.warn(f"User {request.user} attempting to delete reviewed app {app}")
|
logger.warning(f"User {request.user} attempting to delete reviewed app {app}")
|
||||||
else:
|
else:
|
||||||
logger.warn(f"User {request.user} not authorized to delete {app}")
|
logger.warning(f"User {request.user} not authorized to delete {app}")
|
||||||
return redirect('hrapplications:index')
|
return redirect('hrapplications:index')
|
||||||
|
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ def hr_application_view(request, app_id):
|
|||||||
logger.info(f"Saved comment by user {request.user} to {app}")
|
logger.info(f"Saved comment by user {request.user} to {app}")
|
||||||
return redirect('hrapplications:view', app_id)
|
return redirect('hrapplications:view', app_id)
|
||||||
else:
|
else:
|
||||||
logger.warn("User %s does not have permission to add ApplicationComments" % request.user)
|
logger.warning("User %s does not have permission to add ApplicationComments" % request.user)
|
||||||
return redirect('hrapplications:view', app_id)
|
return redirect('hrapplications:view', app_id)
|
||||||
else:
|
else:
|
||||||
logger.debug("Returning blank HRApplication comment form.")
|
logger.debug("Returning blank HRApplication comment form.")
|
||||||
@@ -171,7 +171,7 @@ def hr_application_approve(request, app_id):
|
|||||||
app.save()
|
app.save()
|
||||||
notify(app.user, "Application Accepted", message="Your application to %s has been approved." % app.form.corp, level="success")
|
notify(app.user, "Application Accepted", message="Your application to %s has been approved." % app.form.corp, level="success")
|
||||||
else:
|
else:
|
||||||
logger.warn(f"User {request.user} not authorized to approve {app}")
|
logger.warning(f"User {request.user} not authorized to approve {app}")
|
||||||
return redirect('hrapplications:index')
|
return redirect('hrapplications:index')
|
||||||
|
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ def hr_application_reject(request, app_id):
|
|||||||
app.save()
|
app.save()
|
||||||
notify(app.user, "Application Rejected", message="Your application to %s has been rejected." % app.form.corp, level="danger")
|
notify(app.user, "Application Rejected", message="Your application to %s has been rejected." % app.form.corp, level="danger")
|
||||||
else:
|
else:
|
||||||
logger.warn(f"User {request.user} not authorized to reject {app}")
|
logger.warning(f"User {request.user} not authorized to reject {app}")
|
||||||
return redirect('hrapplications:index')
|
return redirect('hrapplications:index')
|
||||||
|
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ def hr_application_search(request):
|
|||||||
app_list = app_list.filter(
|
app_list = app_list.filter(
|
||||||
form__corp__corporation_id=request.user.profile.main_character.corporation_id)
|
form__corp__corporation_id=request.user.profile.main_character.corporation_id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.warn(
|
logger.warning(
|
||||||
"User %s missing main character model: unable to filter applications to search" % request.user)
|
"User %s missing main character model: unable to filter applications to search" % request.user)
|
||||||
|
|
||||||
applications = app_list.filter(
|
applications = app_list.filter(
|
||||||
@@ -219,7 +219,7 @@ def hr_application_search(request):
|
|||||||
Q(user__character_ownerships__character__corporation_name__icontains=searchstring) |
|
Q(user__character_ownerships__character__corporation_name__icontains=searchstring) |
|
||||||
Q(user__character_ownerships__character__alliance_name__icontains=searchstring) |
|
Q(user__character_ownerships__character__alliance_name__icontains=searchstring) |
|
||||||
Q(user__username__icontains=searchstring)
|
Q(user__username__icontains=searchstring)
|
||||||
)
|
).distinct()
|
||||||
|
|
||||||
context = {'applications': applications, 'search_form': HRApplicationSearchForm()}
|
context = {'applications': applications, 'search_form': HRApplicationSearchForm()}
|
||||||
|
|
||||||
@@ -246,6 +246,6 @@ def hr_application_mark_in_progress(request, app_id):
|
|||||||
app.save()
|
app.save()
|
||||||
notify(app.user, "Application In Progress", message=f"Your application to {app.form.corp} is being reviewed by {app.reviewer_str}")
|
notify(app.user, "Application In Progress", message=f"Your application to {app.form.corp} is being reviewed by {app.reviewer_str}")
|
||||||
else:
|
else:
|
||||||
logger.warn(
|
logger.warning(
|
||||||
f"User {request.user} unable to mark {app} in progress: already being reviewed by {app.reviewer}")
|
f"User {request.user} unable to mark {app} in progress: already being reviewed by {app.reviewer}")
|
||||||
return redirect("hrapplications:view", app_id)
|
return redirect("hrapplications:view", app_id)
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
allianceauth/locale/uk/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/uk/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2410
allianceauth/locale/uk/LC_MESSAGES/django.po
Normal file
2410
allianceauth/locale/uk/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Notifications" %}{% endblock %}
|
{% block page_title %}{% translate "Notifications" %}{% endblock %}
|
||||||
@@ -9,15 +8,15 @@
|
|||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
|
||||||
<div class="panel-heading">
|
<div class="panel-heading clearfix">
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills navbar-left">
|
||||||
<li class="active"><a data-toggle="tab" href="#unread">{% translate "Unread" %}<b>({{ unread|length }})</b></a></li>
|
<li class="active"><a data-toggle="tab" href="#unread">{% translate "Unread" %}<b>({{ unread|length }})</b></a></li>
|
||||||
<li><a data-toggle="tab" href="#read">{% translate "Read" %} <b>({{ read|length }})</b></a></li>
|
<li><a data-toggle="tab" href="#read">{% translate "Read" %} <b>({{ read|length }})</b></a></li>
|
||||||
<div class="pull-right">
|
|
||||||
<a href="{% url 'notifications:mark_all_read' %}" class="btn btn-warning">{% translate "Mark All Read" %}</a>
|
|
||||||
<a href="{% url 'notifications:delete_all_read' %}" class="btn btn-danger">{% translate "Delete All Read" %}</a>
|
|
||||||
</div>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="nav navbar-nav navbar-right" style="margin-right: 0;">
|
||||||
|
<a href="{% url 'notifications:mark_all_read' %}" class="btn btn-warning">{% translate "Mark All Read" %}</a>
|
||||||
|
<a href="{% url 'notifications:delete_all_read' %}" class="btn btn-danger">{% translate "Delete All Read" %}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "View Notification" %}{% endblock page_title %}
|
{% block page_title %}{% translate "View Notification" %}{% endblock page_title %}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ def notification_view(request, notif_id):
|
|||||||
notif.mark_viewed()
|
notif.mark_viewed()
|
||||||
return render(request, 'notifications/view.html', context)
|
return render(request, 'notifications/view.html', context)
|
||||||
else:
|
else:
|
||||||
logger.warn(
|
logger.warning(
|
||||||
"User %s not authorized to view notif_id %s belonging to user %s",
|
"User %s not authorized to view notif_id %s belonging to user %s",
|
||||||
request.user,
|
request.user,
|
||||||
notif_id, notif.user
|
notif_id, notif.user
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class OpTimer(models.Model):
|
|||||||
fc = models.CharField(max_length=254, default="")
|
fc = models.CharField(max_length=254, default="")
|
||||||
post_time = models.DateTimeField(default=timezone.now)
|
post_time = models.DateTimeField(default=timezone.now)
|
||||||
eve_character = models.ForeignKey(EveCharacter, null=True,
|
eve_character = models.ForeignKey(EveCharacter, null=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
description = models.TextField(blank=True, default="")
|
description = models.TextField(blank=True, default="")
|
||||||
type = models.ForeignKey(OpTimerType, null=True, on_delete=models.SET_NULL)
|
type = models.ForeignKey(OpTimerType, null=True, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
|
||||||
@@ -40,9 +39,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'bundles/moment-js.html' with locale=True %}
|
{% include 'bundles/moment-js.html' with locale=True %}
|
||||||
<script src="{% static 'js/timers.js' %}"></script>
|
{% include 'bundles/timers-js.html' %}
|
||||||
|
|
||||||
<script type="application/javascript">
|
<script>
|
||||||
// Data
|
// Data
|
||||||
let timers = [
|
let timers = [
|
||||||
{% for op in optimer %}
|
{% for op in optimer %}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{{ permission.permission.codename }} - {% translate "Permissions Audit" %}{% endblock page_title %}
|
{% block page_title %}{{ permission.permission.codename }} - {% translate "Permissions Audit" %}{% endblock page_title %}
|
||||||
@@ -47,7 +45,7 @@
|
|||||||
|
|
||||||
{% block extra_javascript %}
|
{% block extra_javascript %}
|
||||||
{% include 'bundles/datatables-js.html' %}
|
{% include 'bundles/datatables-js.html' %}
|
||||||
<script type="application/javascript" src="{% static 'js/filterDropDown/filterDropDown.min.js' %}"></script>
|
{% include 'bundles/filterdropdown-js.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
{{ type }}: {{ name }}
|
{{ type }}: {{ name }}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<img src="{{ user.profile.main_character|character_portrait_url:32 }}" class="img-circle">
|
<img src="{{ user.profile.main_character|character_portrait_url:32 }}" class="img-circle" alt="{{ user.profile.main_character.character_name }}">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<strong>{{ user }}<br></strong>
|
<strong>{{ user }}<br></strong>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{% extends "allianceauth/base.html" %}
|
{% extends "allianceauth/base.html" %}
|
||||||
{% load bootstrap %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Permissions Overview" %}{% endblock page_title %}
|
{% block page_title %}{% translate "Permissions Overview" %}{% endblock page_title %}
|
||||||
@@ -10,10 +8,10 @@
|
|||||||
<h1 class="page-header">{% translate "Permissions Overview" %}</h1>
|
<h1 class="page-header">{% translate "Permissions Overview" %}</h1>
|
||||||
<p>
|
<p>
|
||||||
{% if request.GET.all != 'yes' %}
|
{% if request.GET.all != 'yes' %}
|
||||||
{% blocktrans %}Showing only applied permissions{% endblocktrans %}
|
{% blocktranslate %}Showing only applied permissions{% endblocktranslate %}
|
||||||
<a href="{% url 'permissions_tool:overview' %}?all=yes" class="btn btn-primary">{% translate "Show All" %}</a>
|
<a href="{% url 'permissions_tool:overview' %}?all=yes" class="btn btn-primary">{% translate "Show All" %}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% blocktrans %}Showing all permissions{% endblocktrans %}
|
{% blocktranslate %}Showing all permissions{% endblocktranslate %}
|
||||||
<a href="{% url 'permissions_tool:overview' %}?all=no" class="btn btn-primary">{% translate "Show Applied" %}</a>
|
<a href="{% url 'permissions_tool:overview' %}?all=no" class="btn btn-primary">{% translate "Show Applied" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
@@ -80,7 +78,7 @@
|
|||||||
|
|
||||||
{% block extra_javascript %}
|
{% block extra_javascript %}
|
||||||
{% include 'bundles/datatables-js.html' %}
|
{% include 'bundles/datatables-js.html' %}
|
||||||
<script type="application/javascript" src="{% static 'js/filterDropDown/filterDropDown.min.js' %}"></script>
|
{% include 'bundles/filterdropdown-js.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
|
|||||||
@@ -84,17 +84,17 @@ LOCALE_PATHS = (
|
|||||||
os.path.join(BASE_DIR, 'locale/'),
|
os.path.join(BASE_DIR, 'locale/'),
|
||||||
)
|
)
|
||||||
|
|
||||||
ugettext = lambda s: s
|
|
||||||
LANGUAGES = (
|
LANGUAGES = (
|
||||||
('en', ugettext('English')),
|
("en", "English"),
|
||||||
('de', ugettext('German')),
|
("de", "German"),
|
||||||
('es', ugettext('Spanish')),
|
("es", "Spanish"),
|
||||||
('zh-hans', ugettext('Chinese Simplified')),
|
("zh-hans", "Chinese Simplified"),
|
||||||
('ru', ugettext('Russian')),
|
("ru", "Russian"),
|
||||||
('ko', ugettext('Korean')),
|
("ko", "Korean"),
|
||||||
('fr', ugettext('French')),
|
("fr", "French"),
|
||||||
('ja', ugettext('Japanese')),
|
("ja", "Japanese"),
|
||||||
('it', ugettext('Italian')),
|
("it", "Italian"),
|
||||||
|
("uk", "Ukrainian"),
|
||||||
)
|
)
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ STATIC_ROOT = "/var/www/{{ project_name }}/static/"
|
|||||||
# in page titles and the site header.
|
# in page titles and the site header.
|
||||||
SITE_NAME = '{{ project_name }}'
|
SITE_NAME = '{{ project_name }}'
|
||||||
|
|
||||||
|
# This is your websites URL, set it accordingly
|
||||||
|
# Make sure this URL is WITHOUT a trailing slash
|
||||||
|
SITE_URL = "https://example.com"
|
||||||
|
|
||||||
|
# Django security
|
||||||
|
CSRF_TRUSTED_ORIGINS = [SITE_URL]
|
||||||
|
|
||||||
# Change this to enable/disable debug mode, which displays
|
# Change this to enable/disable debug mode, which displays
|
||||||
# useful error messages but can leak sensitive data.
|
# useful error messages but can leak sensitive data.
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
@@ -39,15 +46,16 @@ DATABASES['default'] = {
|
|||||||
|
|
||||||
# Register an application at https://developers.eveonline.com for Authentication
|
# Register an application at https://developers.eveonline.com for Authentication
|
||||||
# & API Access and fill out these settings. Be sure to set the callback URL
|
# & API Access and fill out these settings. Be sure to set the callback URL
|
||||||
# to https://example.com/sso/callback substituting your domain for example.com
|
# to https://example.com/sso/callback substituting your domain for example.com in
|
||||||
|
# CCP's developer portal
|
||||||
# Logging in to auth requires the publicData scope (can be overridden through the
|
# Logging in to auth requires the publicData scope (can be overridden through the
|
||||||
# LOGIN_TOKEN_SCOPES setting). Other apps may require more (see their docs).
|
# LOGIN_TOKEN_SCOPES setting). Other apps may require more (see their docs).
|
||||||
ESI_SSO_CLIENT_ID = ''
|
ESI_SSO_CLIENT_ID = ''
|
||||||
ESI_SSO_CLIENT_SECRET = ''
|
ESI_SSO_CLIENT_SECRET = ''
|
||||||
ESI_SSO_CALLBACK_URL = ''
|
ESI_SSO_CALLBACK_URL = f"{SITE_URL}/sso/callback"
|
||||||
ESI_USER_CONTACT_EMAIL = '' # A server maintainer that CCP can contact in case of issues.
|
ESI_USER_CONTACT_EMAIL = '' # A server maintainer that CCP can contact in case of issues.
|
||||||
|
|
||||||
# By default emails are validated before new users can log in.
|
# By default, emails are validated before new users can log in.
|
||||||
# It's recommended to use a free service like SparkPost or Elastic Email to send email.
|
# It's recommended to use a free service like SparkPost or Elastic Email to send email.
|
||||||
# https://www.sparkpost.com/docs/integrations/django/
|
# https://www.sparkpost.com/docs/integrations/django/
|
||||||
# https://elasticemail.com/resources/settings/smtp-api/
|
# https://elasticemail.com/resources/settings/smtp-api/
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from allianceauth import hooks
|
from allianceauth import hooks
|
||||||
from allianceauth.authentication.admin import (
|
from allianceauth.authentication.admin import (
|
||||||
|
MainAllianceFilter,
|
||||||
|
MainCorporationsFilter,
|
||||||
|
user_main_organization,
|
||||||
user_profile_pic,
|
user_profile_pic,
|
||||||
user_username,
|
user_username,
|
||||||
user_main_organization,
|
|
||||||
MainCorporationsFilter,
|
|
||||||
MainAllianceFilter
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from .models import NameFormatConfig
|
from .models import NameFormatConfig
|
||||||
@@ -17,7 +17,7 @@ class ServicesUserAdmin(admin.ModelAdmin):
|
|||||||
"""Parent class for UserAdmin classes for all services"""
|
"""Parent class for UserAdmin classes for all services"""
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
"all": ("services/admin.css",)
|
"all": ("allianceauth/services/admin.css",)
|
||||||
}
|
}
|
||||||
|
|
||||||
search_fields = ('user__username',)
|
search_fields = ('user__username',)
|
||||||
@@ -36,19 +36,18 @@ class ServicesUserAdmin(admin.ModelAdmin):
|
|||||||
MainAllianceFilter,
|
MainAllianceFilter,
|
||||||
'user__date_joined',
|
'user__date_joined',
|
||||||
)
|
)
|
||||||
|
list_select_related = (
|
||||||
|
'user', 'user__profile__main_character', 'user__profile__state'
|
||||||
|
)
|
||||||
|
|
||||||
|
@admin.display(ordering='user__profile__state__name')
|
||||||
def _state(self, obj):
|
def _state(self, obj):
|
||||||
return obj.user.profile.state.name
|
return obj.user.profile.state.name
|
||||||
|
|
||||||
_state.short_description = 'state'
|
@admin.display(ordering='user__date_joined')
|
||||||
_state.admin_order_field = 'user__profile__state__name'
|
|
||||||
|
|
||||||
def _date_joined(self, obj):
|
def _date_joined(self, obj):
|
||||||
return obj.user.date_joined
|
return obj.user.date_joined
|
||||||
|
|
||||||
_date_joined.short_description = 'date joined'
|
|
||||||
_date_joined.admin_order_field = 'user__date_joined'
|
|
||||||
|
|
||||||
|
|
||||||
class NameFormatConfigForm(forms.ModelForm):
|
class NameFormatConfigForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -62,6 +61,7 @@ class NameFormatConfigForm(forms.ModelForm):
|
|||||||
self.fields['service_name'] = forms.ChoiceField(choices=SERVICE_CHOICES)
|
self.fields['service_name'] = forms.ChoiceField(choices=SERVICE_CHOICES)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(NameFormatConfig)
|
||||||
class NameFormatConfigAdmin(admin.ModelAdmin):
|
class NameFormatConfigAdmin(admin.ModelAdmin):
|
||||||
form = NameFormatConfigForm
|
form = NameFormatConfigForm
|
||||||
list_display = ('service_name', 'get_state_display_string')
|
list_display = ('service_name', 'get_state_display_string')
|
||||||
@@ -69,6 +69,3 @@ class NameFormatConfigAdmin(admin.ModelAdmin):
|
|||||||
def get_state_display_string(self, obj):
|
def get_state_display_string(self, obj):
|
||||||
return ', '.join([state.name for state in obj.states.all()])
|
return ', '.join([state.name for state in obj.states.all()])
|
||||||
get_state_display_string.short_description = 'States'
|
get_state_display_string.short_description = 'States'
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(NameFormatConfig, NameFormatConfigAdmin)
|
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ import logging
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from . import __title__
|
|
||||||
from ...admin import ServicesUserAdmin
|
from ...admin import ServicesUserAdmin
|
||||||
|
from . import __title__
|
||||||
from .models import DiscordUser
|
from .models import DiscordUser
|
||||||
from .utils import LoggerAddTag
|
from .utils import LoggerAddTag
|
||||||
|
|
||||||
|
|
||||||
logger = LoggerAddTag(logging.getLogger(__name__), __title__)
|
logger = LoggerAddTag(logging.getLogger(__name__), __title__)
|
||||||
|
|
||||||
|
|
||||||
@@ -18,21 +17,16 @@ class DiscordUserAdmin(ServicesUserAdmin):
|
|||||||
list_filter = ServicesUserAdmin.list_filter + ('activated',)
|
list_filter = ServicesUserAdmin.list_filter + ('activated',)
|
||||||
ordering = ('-activated',)
|
ordering = ('-activated',)
|
||||||
|
|
||||||
def _uid(self, obj):
|
|
||||||
return obj.uid
|
|
||||||
|
|
||||||
_uid.short_description = 'Discord ID (UID)'
|
|
||||||
_uid.admin_order_field = 'uid'
|
|
||||||
|
|
||||||
def _username(self, obj):
|
|
||||||
if obj.username and obj.discriminator:
|
|
||||||
return f'{obj.username}#{obj.discriminator}'
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def delete_queryset(self, request, queryset):
|
def delete_queryset(self, request, queryset):
|
||||||
for user in queryset:
|
for user in queryset:
|
||||||
user.delete_user()
|
user.delete_user()
|
||||||
|
|
||||||
_username.short_description = 'Discord Username'
|
@admin.display(description='Discord ID (UID)', ordering='uid')
|
||||||
_username.admin_order_field = 'username'
|
def _uid(self, obj):
|
||||||
|
return obj.uid
|
||||||
|
|
||||||
|
@admin.display(description='Discord Username', ordering='username')
|
||||||
|
def _username(self, obj):
|
||||||
|
if obj.username and obj.discriminator:
|
||||||
|
return f'{obj.username}#{obj.discriminator}'
|
||||||
|
return ''
|
||||||
|
|||||||
37
allianceauth/services/modules/discord/api.py
Normal file
37
allianceauth/services/modules/discord/api.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"""Public interface for community apps who want to interact with the Discord server
|
||||||
|
of the current Alliance Auth instance.
|
||||||
|
|
||||||
|
Example
|
||||||
|
=======
|
||||||
|
|
||||||
|
Here is an example for using the api to fetch the current roles from the configured Discord server.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from allianceauth.services.modules.discord.api import create_bot_client, discord_guild_id
|
||||||
|
|
||||||
|
client = create_bot_client() # create a new Discord client
|
||||||
|
guild_id = discord_guild_id() # get the ID of the configured Discord server
|
||||||
|
roles = client.guild_roles(guild_id) # fetch the roles from our Discord server
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
The docs for the client class can be found here: :py:class:`~allianceauth.services.modules.discord.discord_client.client.DiscordClient`
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from .app_settings import DISCORD_GUILD_ID
|
||||||
|
from .core import create_bot_client, group_to_role, server_name # noqa
|
||||||
|
from .discord_client.models import Role # noqa
|
||||||
|
from .models import DiscordUser # noqa
|
||||||
|
|
||||||
|
__all__ = ["create_bot_client", "group_to_role", "server_name", "DiscordUser", "Role"]
|
||||||
|
|
||||||
|
|
||||||
|
def discord_guild_id() -> Optional[int]:
|
||||||
|
"""Guild ID of configured Discord server.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Guild ID or ``None`` if not configured
|
||||||
|
"""
|
||||||
|
return int(DISCORD_GUILD_ID) if DISCORD_GUILD_ID else None
|
||||||
@@ -2,16 +2,25 @@ from .utils import clean_setting
|
|||||||
|
|
||||||
|
|
||||||
DISCORD_APP_ID = clean_setting('DISCORD_APP_ID', '')
|
DISCORD_APP_ID = clean_setting('DISCORD_APP_ID', '')
|
||||||
|
"""App ID for the AA bot on Discord. Needs to be set."""
|
||||||
|
|
||||||
DISCORD_APP_SECRET = clean_setting('DISCORD_APP_SECRET', '')
|
DISCORD_APP_SECRET = clean_setting('DISCORD_APP_SECRET', '')
|
||||||
|
"""App secret for the AA bot on Discord. Needs to be set."""
|
||||||
|
|
||||||
DISCORD_BOT_TOKEN = clean_setting('DISCORD_BOT_TOKEN', '')
|
DISCORD_BOT_TOKEN = clean_setting('DISCORD_BOT_TOKEN', '')
|
||||||
|
"""Token used by the AA bot on Discord. Needs to be set."""
|
||||||
|
|
||||||
DISCORD_CALLBACK_URL = clean_setting('DISCORD_CALLBACK_URL', '')
|
DISCORD_CALLBACK_URL = clean_setting('DISCORD_CALLBACK_URL', '')
|
||||||
|
"""Callback URL for OAuth with Discord. Needs to be set."""
|
||||||
|
|
||||||
DISCORD_GUILD_ID = clean_setting('DISCORD_GUILD_ID', '')
|
DISCORD_GUILD_ID = clean_setting('DISCORD_GUILD_ID', '')
|
||||||
|
"""ID of the Discord Server. Needs to be set."""
|
||||||
|
|
||||||
# max retries of tasks after an error occurred
|
|
||||||
DISCORD_TASKS_MAX_RETRIES = clean_setting('DISCORD_TASKS_MAX_RETRIES', 3)
|
DISCORD_TASKS_MAX_RETRIES = clean_setting('DISCORD_TASKS_MAX_RETRIES', 3)
|
||||||
|
"""Max retries of tasks after an error occurred."""
|
||||||
|
|
||||||
# Pause in seconds until next retry for tasks after the API returned an error
|
|
||||||
DISCORD_TASKS_RETRY_PAUSE = clean_setting('DISCORD_TASKS_RETRY_PAUSE', 60)
|
DISCORD_TASKS_RETRY_PAUSE = clean_setting('DISCORD_TASKS_RETRY_PAUSE', 60)
|
||||||
|
"""Pause in seconds until next retry for tasks after the API returned an error."""
|
||||||
|
|
||||||
# automatically sync Discord users names to user's main character name when created
|
|
||||||
DISCORD_SYNC_NAMES = clean_setting('DISCORD_SYNC_NAMES', False)
|
DISCORD_SYNC_NAMES = clean_setting('DISCORD_SYNC_NAMES', False)
|
||||||
|
"""Automatically sync Discord users names to user's main character name when created."""
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from django.template.loader import render_to_string
|
|||||||
from allianceauth import hooks
|
from allianceauth import hooks
|
||||||
from allianceauth.services.hooks import ServicesHook
|
from allianceauth.services.hooks import ServicesHook
|
||||||
|
|
||||||
|
from .core import server_name, user_formatted_nick
|
||||||
from .models import DiscordUser
|
from .models import DiscordUser
|
||||||
from .urls import urlpatterns
|
from .urls import urlpatterns
|
||||||
from .utils import LoggerAddTag
|
from .utils import LoggerAddTag
|
||||||
@@ -53,7 +54,7 @@ class DiscordService(ServicesHook):
|
|||||||
return render_to_string(
|
return render_to_string(
|
||||||
self.service_ctrl_template,
|
self.service_ctrl_template,
|
||||||
{
|
{
|
||||||
'server_name': DiscordUser.objects.server_name(),
|
'server_name': server_name(),
|
||||||
'user_has_account': user_has_account,
|
'user_has_account': user_has_account,
|
||||||
'discord_username': discord_username
|
'discord_username': discord_username
|
||||||
},
|
},
|
||||||
@@ -73,7 +74,7 @@ class DiscordService(ServicesHook):
|
|||||||
'user_pk': user.pk,
|
'user_pk': user.pk,
|
||||||
# since the new nickname is not yet in the DB we need to
|
# since the new nickname is not yet in the DB we need to
|
||||||
# provide it manually to the task
|
# provide it manually to the task
|
||||||
'nickname': DiscordUser.objects.user_formatted_nick(user)
|
'nickname': user_formatted_nick(user)
|
||||||
},
|
},
|
||||||
priority=SINGLE_TASK_PRIORITY
|
priority=SINGLE_TASK_PRIORITY
|
||||||
)
|
)
|
||||||
|
|||||||
129
allianceauth/services/modules/discord/core.py
Normal file
129
allianceauth/services/modules/discord/core.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
"""Core functionality of the Discord service not directly related to models."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
|
from requests.exceptions import HTTPError
|
||||||
|
|
||||||
|
from django.contrib.auth.models import Group, User
|
||||||
|
|
||||||
|
from allianceauth.groupmanagement.models import ReservedGroupName
|
||||||
|
from allianceauth.services.hooks import NameFormatter
|
||||||
|
|
||||||
|
from . import __title__
|
||||||
|
from .app_settings import DISCORD_BOT_TOKEN, DISCORD_GUILD_ID
|
||||||
|
from .discord_client import DiscordClient, RolesSet, Role
|
||||||
|
from .discord_client.exceptions import DiscordClientException
|
||||||
|
from .utils import LoggerAddTag
|
||||||
|
|
||||||
|
logger = LoggerAddTag(logging.getLogger(__name__), __title__)
|
||||||
|
|
||||||
|
|
||||||
|
def create_bot_client(is_rate_limited: bool = True) -> DiscordClient:
|
||||||
|
"""Create new bot client for accessing the configured Discord server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
is_rate_limited: Set to False to turn off rate limiting (use with care).
|
||||||
|
|
||||||
|
Return:
|
||||||
|
Discord client instance
|
||||||
|
"""
|
||||||
|
return DiscordClient(DISCORD_BOT_TOKEN, is_rate_limited=is_rate_limited)
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_roles_for_user(
|
||||||
|
user: User,
|
||||||
|
client: DiscordClient,
|
||||||
|
discord_uid: int,
|
||||||
|
state_name: str = None,
|
||||||
|
) -> Tuple[RolesSet, Optional[bool]]:
|
||||||
|
"""Calculate current Discord roles for an Auth user.
|
||||||
|
|
||||||
|
Takes into account reserved groups and existing managed roles (e.g. nitro).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- Discord roles, changed flag:
|
||||||
|
- True when roles have changed,
|
||||||
|
- False when they have not changed,
|
||||||
|
- None if user is not a member of the guild
|
||||||
|
"""
|
||||||
|
roles_calculated = client.match_or_create_roles_from_names_2(
|
||||||
|
guild_id=DISCORD_GUILD_ID,
|
||||||
|
role_names=_user_group_names(user=user, state_name=state_name),
|
||||||
|
)
|
||||||
|
logger.debug("Calculated roles for user %s: %s", user, roles_calculated.ids())
|
||||||
|
roles_current = client.guild_member_roles(
|
||||||
|
guild_id=DISCORD_GUILD_ID, user_id=discord_uid
|
||||||
|
)
|
||||||
|
if roles_current is None:
|
||||||
|
logger.debug("User %s is not a member of the guild.", user)
|
||||||
|
return roles_calculated, None
|
||||||
|
logger.debug("Current roles user %s: %s", user, roles_current.ids())
|
||||||
|
reserved_role_names = ReservedGroupName.objects.values_list("name", flat=True)
|
||||||
|
roles_reserved = roles_current.subset(role_names=reserved_role_names)
|
||||||
|
roles_managed = roles_current.subset(managed_only=True)
|
||||||
|
roles_persistent = roles_managed.union(roles_reserved)
|
||||||
|
if roles_calculated == roles_current.difference(roles_persistent):
|
||||||
|
return roles_calculated, False
|
||||||
|
return roles_calculated.union(roles_persistent), True
|
||||||
|
|
||||||
|
|
||||||
|
def _user_group_names(user: User, state_name: str = None) -> List[str]:
|
||||||
|
"""Names of groups and state the given user is a member of."""
|
||||||
|
if not state_name:
|
||||||
|
state_name = user.profile.state.name
|
||||||
|
group_names = [group.name for group in user.groups.all()] + [state_name]
|
||||||
|
logger.debug("Group names for roles updates of user %s are: %s", user, group_names)
|
||||||
|
return group_names
|
||||||
|
|
||||||
|
|
||||||
|
def user_formatted_nick(user: User) -> Optional[str]:
|
||||||
|
"""Name of the given user's main character with name formatting applied.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Name or ``None`` if user has no main.
|
||||||
|
"""
|
||||||
|
from .auth_hooks import DiscordService
|
||||||
|
|
||||||
|
if user.profile.main_character:
|
||||||
|
return NameFormatter(DiscordService(), user).format_name()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def group_to_role(group: Group) -> Optional[Role]:
|
||||||
|
"""Fetch the Discord role matching the given Django group by name.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Discord role or None if no matching role exist
|
||||||
|
"""
|
||||||
|
return default_bot_client.match_role_from_name(
|
||||||
|
guild_id=DISCORD_GUILD_ID, role_name=group.name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def server_name(use_cache: bool = True) -> str:
|
||||||
|
"""Fetches the name of the current Discord server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
use_cache: When set False will force an API call to get the server name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Server name or an empty string if the name could not be retrieved
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
server_name = default_bot_client.guild_name(
|
||||||
|
guild_id=DISCORD_GUILD_ID, use_cache=use_cache
|
||||||
|
)
|
||||||
|
except (HTTPError, DiscordClientException):
|
||||||
|
server_name = ""
|
||||||
|
except Exception:
|
||||||
|
logger.warning(
|
||||||
|
"Unexpected error when trying to retrieve the server name from Discord",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
server_name = ""
|
||||||
|
return server_name
|
||||||
|
|
||||||
|
|
||||||
|
# Default bot client to be used by modules of this package
|
||||||
|
default_bot_client = create_bot_client()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user