mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 14:16:21 +01:00
Compare commits
387 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
179c26975c | ||
|
|
e17f6e799b | ||
|
|
7cd8294104 | ||
|
|
ede5540335 | ||
|
|
747279b773 | ||
|
|
44f8b1c477 | ||
|
|
7c6ebd9bf6 | ||
|
|
430469b708 | ||
|
|
efbb3cee31 | ||
|
|
21094ed4dd | ||
|
|
5f326efc7e | ||
|
|
b6e34ace35 | ||
|
|
fe4a8965e3 | ||
|
|
23371c233d | ||
|
|
7a3bbf0d7f | ||
|
|
89a1bec9c1 | ||
|
|
1c1e70619a | ||
|
|
0ff4374efa | ||
|
|
18d0e58a48 | ||
|
|
84f44338dc | ||
|
|
2ba0412890 | ||
|
|
2326522b29 | ||
|
|
a7cb6ee434 | ||
|
|
2aeef63565 | ||
|
|
3c9e7335ef | ||
|
|
49067de325 | ||
|
|
471e7e29ae | ||
|
|
3ec5775406 | ||
|
|
e804d2b60d | ||
|
|
742438a95d | ||
|
|
5c60086baa | ||
|
|
e49041bb14 | ||
|
|
f3cbe91883 | ||
|
|
ea439a2176 | ||
|
|
56e1e76f11 | ||
|
|
634e7357be | ||
|
|
08dc88da1a | ||
|
|
3d206e445c | ||
|
|
64686cdad1 | ||
|
|
d7fe09bdf1 | ||
|
|
6da50da92f | ||
|
|
51e4dd986f | ||
|
|
bee6522182 | ||
|
|
1711a9dd33 | ||
|
|
3914626379 | ||
|
|
df276cb32d | ||
|
|
daad7d8b10 | ||
|
|
3bf5bc0fe3 | ||
|
|
96abae553a | ||
|
|
f9cbfb1562 | ||
|
|
8eaa94e179 | ||
|
|
4f876b648b | ||
|
|
cd738137c0 | ||
|
|
5605eb129d | ||
|
|
87ef0f21a3 | ||
|
|
a1c7ce827e | ||
|
|
97466bcdfb | ||
|
|
ff3096b106 | ||
|
|
98f0d77f3f | ||
|
|
92548ba402 | ||
|
|
c46741d311 | ||
|
|
7c7c1abf7c | ||
|
|
fc303b1b0a | ||
|
|
4e220a9679 | ||
|
|
b17b1f7504 | ||
|
|
7081fc0e76 | ||
|
|
68e4574f19 | ||
|
|
e6e0a70012 | ||
|
|
13e38da942 | ||
|
|
468c1de26b | ||
|
|
22ef5ac0e5 | ||
|
|
ef2dc08958 | ||
|
|
6b84ffa16c | ||
|
|
d7a1096413 | ||
|
|
93b94a8bc2 | ||
|
|
9a95716105 | ||
|
|
dbfcf5d87a | ||
|
|
105d7d53b3 | ||
|
|
01cefe1457 | ||
|
|
7fe3db8017 | ||
|
|
e0945fac80 | ||
|
|
40fa190820 | ||
|
|
670580f8f3 | ||
|
|
323a0bcf16 | ||
|
|
6e995edd80 | ||
|
|
8d86e45b7a | ||
|
|
2aa6df4461 | ||
|
|
cf6f989502 | ||
|
|
3e1d8ae334 | ||
|
|
bcfe9484b5 | ||
|
|
5e4d1b9cfd | ||
|
|
3b463e7305 | ||
|
|
eedf5082fa | ||
|
|
2ea5b15175 | ||
|
|
7a9808aad3 | ||
|
|
a1d712694c | ||
|
|
ca11c726a3 | ||
|
|
6e0f7a35bd | ||
|
|
7375b001ca | ||
|
|
0287086633 | ||
|
|
9eb2becbb5 | ||
|
|
12f1444fe7 | ||
|
|
d6372bd093 | ||
|
|
3935a9cdd2 | ||
|
|
49fb6c39d5 | ||
|
|
8821f18b21 | ||
|
|
f4d8ead54e | ||
|
|
7427e13505 | ||
|
|
445683c3d5 | ||
|
|
677c46c48a | ||
|
|
87b9e3f87a | ||
|
|
da2a5aff2f | ||
|
|
65d77743dc | ||
|
|
1c7f8256d0 | ||
|
|
61f0aae5d9 | ||
|
|
4d56030edf | ||
|
|
2dfe194a3b | ||
|
|
ebb40deb7f | ||
|
|
a3a2d3d35b | ||
|
|
d4dee519b8 | ||
|
|
dfcbad3476 | ||
|
|
e03c1307a3 | ||
|
|
054ef27fa4 | ||
|
|
97e224b8e6 | ||
|
|
3b8fa415bc | ||
|
|
b94fd7ed19 | ||
|
|
d1dac61135 | ||
|
|
d2a095217f | ||
|
|
3a95b89779 | ||
|
|
4f5b231bdf | ||
|
|
40c0b8d862 | ||
|
|
62c936f1c0 | ||
|
|
2a762df9b3 | ||
|
|
8fb5a488f7 | ||
|
|
dc239d5396 | ||
|
|
2815ebaa07 | ||
|
|
34dd4802a8 | ||
|
|
3ebf11308c | ||
|
|
5cd5180a91 | ||
|
|
ba213db493 | ||
|
|
8362d11714 | ||
|
|
7ba65968ed | ||
|
|
7fdbac20cb | ||
|
|
76efcb5266 | ||
|
|
6aacb8c2e3 | ||
|
|
465fba3a18 | ||
|
|
9ae6addc71 | ||
|
|
76ae9b8849 | ||
|
|
13a8b7678f | ||
|
|
c166fc0ef9 | ||
|
|
9da61588eb | ||
|
|
7425176b3f | ||
|
|
2bb518f7e0 | ||
|
|
84d5693583 | ||
|
|
867e2a1ded | ||
|
|
fd6c6991f5 | ||
|
|
333fe8497d | ||
|
|
8a3fb17147 | ||
|
|
f8baeb19a7 | ||
|
|
10096862e5 | ||
|
|
89193d2fcf | ||
|
|
fd08b987bd | ||
|
|
4e9e22cb4b | ||
|
|
f8fbbb5ba7 | ||
|
|
dd3ef41396 | ||
|
|
d5fda05dc9 | ||
|
|
d815fad0e6 | ||
|
|
e109198782 | ||
|
|
fbd4672454 | ||
|
|
a29bd567c2 | ||
|
|
960aef95ad | ||
|
|
4aae5497bb | ||
|
|
6081cbe900 | ||
|
|
5e9b47cf79 | ||
|
|
853826c140 | ||
|
|
ce0d8342e3 | ||
|
|
006785e592 | ||
|
|
df05070a55 | ||
|
|
e81450baf3 | ||
|
|
24b6c19aca | ||
|
|
9f4bf13cc9 | ||
|
|
2a156302f0 | ||
|
|
c4d3bde106 | ||
|
|
9c7de58989 | ||
|
|
0900806f68 | ||
|
|
a0d32d8c2d | ||
|
|
e16252842a | ||
|
|
950ae34093 | ||
|
|
0ba94c53aa | ||
|
|
96cc55d174 | ||
|
|
a9062c4389 | ||
|
|
51b86f88b9 | ||
|
|
8184461b48 | ||
|
|
ca0cdd6e15 | ||
|
|
036a17ad3b | ||
|
|
2418023ddd | ||
|
|
c602cf0b00 | ||
|
|
3de988369f | ||
|
|
b28b51916c | ||
|
|
0db0978d5f | ||
|
|
0531da1128 | ||
|
|
547d047f59 | ||
|
|
5cd92e29d3 | ||
|
|
bb44194cfc | ||
|
|
aa1cb96c8a | ||
|
|
40ae092306 | ||
|
|
b2e70c3702 | ||
|
|
eebeb26001 | ||
|
|
003a67224e | ||
|
|
d3a3810456 | ||
|
|
f55008559e | ||
|
|
dbc19c76c5 | ||
|
|
ced7972962 | ||
|
|
ad508bd880 | ||
|
|
5c128f2c78 | ||
|
|
1caaca86cf | ||
|
|
33ad1413d5 | ||
|
|
cbb5c80b94 | ||
|
|
d2edd288f9 | ||
|
|
21e80f6961 | ||
|
|
a747951d19 | ||
|
|
4cc7135ace | ||
|
|
6a990c11e6 | ||
|
|
3f47cecbfc | ||
|
|
77c126ea2f | ||
|
|
6e3219fd1b | ||
|
|
8aeb061635 | ||
|
|
84e2107b62 | ||
|
|
8b7e57494c | ||
|
|
20fcf5efa4 | ||
|
|
8ff3d854ba | ||
|
|
49ff355d50 | ||
|
|
c15b955d5e | ||
|
|
65e1545a66 | ||
|
|
c558a980e1 | ||
|
|
bd8ef84862 | ||
|
|
6f670da1db | ||
|
|
c558f3785f | ||
|
|
04cdd4c64f | ||
|
|
42e96d2f14 | ||
|
|
00fcebd8e3 | ||
|
|
995c84481c | ||
|
|
4dd42da993 | ||
|
|
8295cc51a3 | ||
|
|
f7e1d7c47e | ||
|
|
234191218a | ||
|
|
de12b49527 | ||
|
|
021b7b2edb | ||
|
|
20b959f273 | ||
|
|
e16bb2a737 | ||
|
|
0a6a6e0bf9 | ||
|
|
f0fe3929d4 | ||
|
|
9a62c729eb | ||
|
|
eaedcd5bb7 | ||
|
|
d742257c74 | ||
|
|
ad92d7a916 | ||
|
|
af22222bdf | ||
|
|
01f49bd125 | ||
|
|
af5930b9d0 | ||
|
|
95ba19a827 | ||
|
|
c75647301f | ||
|
|
adedf7534f | ||
|
|
d0684862fe | ||
|
|
2440e5d2b2 | ||
|
|
5f51ad4a6a | ||
|
|
0bd3acc230 | ||
|
|
11a32c90a8 | ||
|
|
6f0b853a60 | ||
|
|
583a6d4c7f | ||
|
|
155494afea | ||
|
|
b0aa58b910 | ||
|
|
1adce85422 | ||
|
|
cd47eadcdc | ||
|
|
247058a30f | ||
|
|
e0fa615e90 | ||
|
|
61ec67183c | ||
|
|
4369813478 | ||
|
|
8bb3d35252 | ||
|
|
398a980fb5 | ||
|
|
e14a295ce6 | ||
|
|
e0cd5c6fb9 | ||
|
|
c37ece49d5 | ||
|
|
8adab8bae0 | ||
|
|
a9c87bc25a | ||
|
|
a4901802c0 | ||
|
|
079dcc5d28 | ||
|
|
10c5a8906d | ||
|
|
e6306ea7aa | ||
|
|
20067c1133 | ||
|
|
dba3c651dc | ||
|
|
0b4d2b819b | ||
|
|
23a3dd1ab9 | ||
|
|
81e5bc5337 | ||
|
|
7eebf4d953 | ||
|
|
9e45d2eac7 | ||
|
|
e3017f1ec7 | ||
|
|
a8ef844fe7 | ||
|
|
12709b1b56 | ||
|
|
51ae604efd | ||
|
|
63071ec359 | ||
|
|
0032e4a01f | ||
|
|
25ab78a41e | ||
|
|
3aa0e323d2 | ||
|
|
cadbb7e61c | ||
|
|
115263eb5a | ||
|
|
18fec5f614 | ||
|
|
02ab064ec3 | ||
|
|
ba25f99cb4 | ||
|
|
28fd1b07ea | ||
|
|
5de19c43df | ||
|
|
9ce1939040 | ||
|
|
322131cd4f | ||
|
|
55e6e92da5 | ||
|
|
e5d29629a5 | ||
|
|
6a0ddc9a83 | ||
|
|
03be66d11f | ||
|
|
26e187e4c8 | ||
|
|
3480c4e0e8 | ||
|
|
1544f097e0 | ||
|
|
2477c31656 | ||
|
|
0dc631d69e | ||
|
|
2a9981cdb9 | ||
|
|
3d92008069 | ||
|
|
59e47c24c2 | ||
|
|
6d942555ff | ||
|
|
7e312bb95f | ||
|
|
c92fee78e2 | ||
|
|
004c48b8ad | ||
|
|
658a8cd6ce | ||
|
|
c1dc130766 | ||
|
|
35f5573b63 | ||
|
|
4d66b7d456 | ||
|
|
77e5747a23 | ||
|
|
a9ebecdec6 | ||
|
|
21f0a96422 | ||
|
|
9e47d19337 | ||
|
|
2c5972d0ab | ||
|
|
6118c0ddec | ||
|
|
ce25deeca1 | ||
|
|
60084de3db | ||
|
|
e16c68e255 | ||
|
|
bf14e9c7c3 | ||
|
|
98e91fe207 | ||
|
|
ee41d62c13 | ||
|
|
346b4014a9 | ||
|
|
9b56a441ed | ||
|
|
068bf1ae7a | ||
|
|
5be686e3ca | ||
|
|
a215b4411c | ||
|
|
e15cfa0fb1 | ||
|
|
46d51699f4 | ||
|
|
ff30a136d5 | ||
|
|
6dcf3304d5 | ||
|
|
beddeea338 | ||
|
|
69723937f7 | ||
|
|
c541f56ee2 | ||
|
|
7e887e5e34 | ||
|
|
072327c79f | ||
|
|
28af3ff11e | ||
|
|
e3b151f2fb | ||
|
|
f87d7dbdf8 | ||
|
|
a04e6ae3d0 | ||
|
|
15042f5e77 | ||
|
|
6e25361d5e | ||
|
|
9e639a0eeb | ||
|
|
257fbdef36 | ||
|
|
df003c8ec5 | ||
|
|
ba22685eb8 | ||
|
|
773288072a | ||
|
|
63afb13d25 | ||
|
|
5dd286bbe7 | ||
|
|
8aaa8172ca | ||
|
|
b68b401146 | ||
|
|
f7821b647f | ||
|
|
a6526d6f78 | ||
|
|
7898594909 | ||
|
|
cfd12ee3cc | ||
|
|
2c9177b19f | ||
|
|
7024552c4e | ||
|
|
a0719e4b86 | ||
|
|
906c589f14 | ||
|
|
ffb526ab0c | ||
|
|
b9d128259e | ||
|
|
13d866bd0d | ||
|
|
ea1887b9ec | ||
|
|
d2f8c2a42f | ||
|
|
200e8f2ff1 |
@@ -19,5 +19,6 @@ exclude_lines =
|
||||
if __name__ == .__main__.:
|
||||
def __repr__
|
||||
raise AssertionError
|
||||
if TYPE_CHECKING:
|
||||
|
||||
ignore_errors = True
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -73,3 +73,4 @@ celerybeat-schedule
|
||||
.flake8
|
||||
.pylintrc
|
||||
Makefile
|
||||
alliance_auth.sqlite3
|
||||
|
||||
@@ -26,11 +26,11 @@ pre-commit-check:
|
||||
<<: *only-default
|
||||
stage: pre-commit
|
||||
image: python:3.11-bullseye
|
||||
variables:
|
||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
cache:
|
||||
paths:
|
||||
- ${PRE_COMMIT_HOME}
|
||||
# variables:
|
||||
# PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
# cache:
|
||||
# paths:
|
||||
# - ${PRE_COMMIT_HOME}
|
||||
script:
|
||||
- pip install pre-commit
|
||||
- pre-commit run --all-files
|
||||
@@ -99,19 +99,6 @@ test-3.11-core:
|
||||
coverage_format: cobertura
|
||||
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
|
||||
|
||||
test-3.12-core:
|
||||
<<: *only-default
|
||||
image: python:3.12-rc-bullseye
|
||||
@@ -123,7 +110,6 @@ test-3.12-core:
|
||||
coverage_report:
|
||||
coverage_format: cobertura
|
||||
path: coverage.xml
|
||||
allow_failure: true
|
||||
|
||||
test-3.8-all:
|
||||
<<: *only-default
|
||||
@@ -174,19 +160,6 @@ test-3.11-all:
|
||||
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
|
||||
|
||||
test-3.12-all:
|
||||
<<: *only-default
|
||||
image: python:3.12-rc-bullseye
|
||||
@@ -198,7 +171,6 @@ test-3.12-all:
|
||||
coverage_report:
|
||||
coverage_format: cobertura
|
||||
path: coverage.xml
|
||||
allow_failure: true
|
||||
|
||||
build-test:
|
||||
stage: test
|
||||
@@ -223,7 +195,7 @@ test-docs:
|
||||
<<: *only-default
|
||||
image: python:3.11-bullseye
|
||||
script:
|
||||
- tox -e docs
|
||||
- tox -e docs
|
||||
|
||||
deploy_production:
|
||||
stage: deploy
|
||||
@@ -259,16 +231,7 @@ build-image:
|
||||
docker run --privileged --rm tonistiigi/binfmt --uninstall qemu-*
|
||||
docker run --privileged --rm tonistiigi/binfmt --install all
|
||||
docker buildx create --use --name new-builder
|
||||
docker buildx build .
|
||||
--tag $IMAGE_TAG
|
||||
--tag $CURRENT_TAG
|
||||
--tag $MINOR_TAG
|
||||
--tag $MAJOR_TAG
|
||||
--tag $LATEST_TAG
|
||||
--file docker/Dockerfile
|
||||
--platform linux/amd64,linux/arm64
|
||||
--push
|
||||
--build-arg AUTH_VERSION=$(echo $CI_COMMIT_TAG | cut -c 2-)
|
||||
docker buildx build . --tag $IMAGE_TAG --tag $CURRENT_TAG --tag $MINOR_TAG --tag $MAJOR_TAG --tag $LATEST_TAG --file docker/Dockerfile --platform linux/amd64,linux/arm64 --push --build-arg AUTH_VERSION=$(echo $CI_COMMIT_TAG | cut -c 2-)
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
when: delayed
|
||||
@@ -288,12 +251,7 @@ build-image-dev:
|
||||
docker run --privileged --rm tonistiigi/binfmt --uninstall qemu-*
|
||||
docker run --privileged --rm tonistiigi/binfmt --install all
|
||||
docker buildx create --use --name new-builder
|
||||
docker buildx build .
|
||||
--tag $IMAGE_TAG
|
||||
--file docker/Dockerfile
|
||||
--platform linux/amd64,linux/arm64
|
||||
--push
|
||||
--build-arg AUTH_PACKAGE=git+https://gitlab.com/allianceauth/allianceauth@$CI_COMMIT_BRANCH
|
||||
docker buildx build . --tag $IMAGE_TAG --file docker/Dockerfile --platform linux/amd64,linux/arm64 --push --build-arg AUTH_PACKAGE=git+https://gitlab.com/allianceauth/allianceauth@$CI_COMMIT_BRANCH
|
||||
rules:
|
||||
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == ""'
|
||||
when: manual
|
||||
@@ -314,12 +272,7 @@ build-image-mr:
|
||||
docker run --privileged --rm tonistiigi/binfmt --uninstall qemu-*
|
||||
docker run --privileged --rm tonistiigi/binfmt --install all
|
||||
docker buildx create --use --name new-builder
|
||||
docker buildx build .
|
||||
--tag $IMAGE_TAG
|
||||
--file docker/Dockerfile
|
||||
--platform linux/amd64,linux/arm64
|
||||
--push
|
||||
--build-arg AUTH_PACKAGE=git+$CI_MERGE_REQUEST_SOURCE_PROJECT_URL@$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
|
||||
docker buildx build . --tag $IMAGE_TAG --file docker/Dockerfile --platform linux/amd64,linux/arm64 --push --build-arg AUTH_PACKAGE=git+$CI_MERGE_REQUEST_SOURCE_PROJECT_URL@$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
when: manual
|
||||
|
||||
@@ -4,8 +4,21 @@
|
||||
# pre-commit autoupdate
|
||||
|
||||
repos:
|
||||
# Code Upgrades
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.15.2
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py38-plus]
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: 1.17.0
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args: [--target-version=4.2]
|
||||
|
||||
# Formatting
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v4.6.0
|
||||
hooks:
|
||||
# Identify invalid files
|
||||
- id: check-ast
|
||||
@@ -13,27 +26,24 @@ repos:
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-xml
|
||||
|
||||
# git checks
|
||||
- id: check-merge-conflict
|
||||
- id: check-added-large-files
|
||||
args: [ --maxkb=1000 ]
|
||||
args: [--maxkb=1000]
|
||||
- id: detect-private-key
|
||||
- id: check-case-conflict
|
||||
|
||||
# Python checks
|
||||
# - id: check-docstring-first
|
||||
# - id: check-docstring-first
|
||||
- id: debug-statements
|
||||
# - id: requirements-txt-fixer
|
||||
# - id: requirements-txt-fixer
|
||||
- id: fix-encoding-pragma
|
||||
args: [ --remove ]
|
||||
args: [--remove]
|
||||
- id: fix-byte-order-marker
|
||||
|
||||
# General quality checks
|
||||
- id: mixed-line-ending
|
||||
args: [ --fix=lf ]
|
||||
args: [--fix=lf]
|
||||
- id: trailing-whitespace
|
||||
args: [ --markdown-linebreak-ext=md ]
|
||||
args: [--markdown-linebreak-ext=md]
|
||||
exclude: |
|
||||
(?x)(
|
||||
\.min\.css|
|
||||
@@ -52,9 +62,8 @@ repos:
|
||||
\.mo|
|
||||
swagger\.json
|
||||
)
|
||||
|
||||
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
||||
rev: 2.7.2
|
||||
rev: 2.7.3
|
||||
hooks:
|
||||
- id: editorconfig-checker
|
||||
exclude: |
|
||||
@@ -65,21 +74,26 @@ repos:
|
||||
\.mo|
|
||||
swagger\.json
|
||||
)
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.10.1
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.41.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [ --py38-plus ]
|
||||
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: 1.14.0
|
||||
- id: markdownlint
|
||||
args:
|
||||
- --disable=MD013
|
||||
# Infrastructure
|
||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||
rev: 2.1.3
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args: [--target-version=4.2]
|
||||
|
||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||
rev: v2.3.0
|
||||
- id: pyproject-fmt
|
||||
name: pyproject.toml formatter
|
||||
description: "Format the pyproject.toml file."
|
||||
args:
|
||||
- --indent=4
|
||||
additional_dependencies:
|
||||
- tox==4.15.0 # https://github.com/tox-dev/tox/releases/latest
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.18
|
||||
hooks:
|
||||
- id: setup-cfg-fmt
|
||||
args: [ --include-version-classifiers ]
|
||||
- id: validate-pyproject
|
||||
name: Validate pyproject.toml
|
||||
description: "Validate the pyproject.toml file."
|
||||
|
||||
@@ -7,11 +7,14 @@ version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
apt_packages:
|
||||
- redis
|
||||
tools:
|
||||
python: "3.8"
|
||||
python: "3.11"
|
||||
jobs:
|
||||
post_system_dependencies:
|
||||
- redis-server --daemonize yes
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
@@ -20,7 +23,7 @@ sphinx:
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats: all
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
# Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- method: pip
|
||||
|
||||
10
.tx/config
10
.tx/config
@@ -1,10 +0,0 @@
|
||||
[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
|
||||
@@ -1,10 +0,0 @@
|
||||
[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
|
||||
10
.tx/transifex.yml
Normal file
10
.tx/transifex.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
filters:
|
||||
- filter_type: file
|
||||
file_format: PO
|
||||
source_file: allianceauth/locale/en/LC_MESSAGES/django.po
|
||||
source_language: en
|
||||
translation_files_expression: allianceauth/locale/<lang>/LC_MESSAGES/django.po
|
||||
|
||||
settings:
|
||||
language_mapping:
|
||||
zh-Hans: zh_Hans
|
||||
@@ -17,7 +17,7 @@ An auth system for EVE Online to help in-game organizations manage online servic
|
||||
- [Documentation](http://allianceauth.rtfd.io)
|
||||
- [Support](#support)
|
||||
- [Release Notes](https://gitlab.com/allianceauth/allianceauth/-/releases)
|
||||
- [Developer Team](#developer-team)
|
||||
- [Developer Team](#development-team)
|
||||
- [Contributing](#contributing)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -5,7 +5,7 @@ manage online service access.
|
||||
# This will make sure the app is always imported when
|
||||
# Django starts so that shared_task will use this app.
|
||||
|
||||
__version__ = '4.0.0a2'
|
||||
__version__ = '4.1.0'
|
||||
__title__ = 'Alliance Auth'
|
||||
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
||||
NAME = f'{__title__} v{__version__}'
|
||||
|
||||
@@ -9,6 +9,8 @@ from .utils import (
|
||||
install_stat_tokens,
|
||||
install_stat_users)
|
||||
|
||||
from allianceauth import __version__
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BASE_URL = "https://www.google-analytics.com"
|
||||
@@ -139,7 +141,7 @@ def send_ga_tracking_celery_event(
|
||||
'client_id': AnalyticsIdentifier.objects.get(id=1).identifier.hex,
|
||||
"user_properties": {
|
||||
"allianceauth_version": {
|
||||
"value": "allianceauth_version"
|
||||
"value": __version__
|
||||
}
|
||||
},
|
||||
'non_personalized_ads': True,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from allianceauth.hooks import DashboardItemHook
|
||||
from allianceauth import hooks
|
||||
from .views import dashboard_characters, dashboard_groups, dashboard_admin
|
||||
from .views import dashboard_characters, dashboard_esi_check, dashboard_groups, dashboard_admin
|
||||
|
||||
|
||||
class UserCharactersHook(DashboardItemHook):
|
||||
@@ -26,6 +26,15 @@ class AdminHook(DashboardItemHook):
|
||||
DashboardItemHook.__init__(
|
||||
self,
|
||||
dashboard_admin,
|
||||
1
|
||||
)
|
||||
|
||||
|
||||
class ESICheckHook(DashboardItemHook):
|
||||
def __init__(self):
|
||||
DashboardItemHook.__init__(
|
||||
self,
|
||||
dashboard_esi_check,
|
||||
0
|
||||
)
|
||||
|
||||
@@ -43,3 +52,8 @@ def register_groups_hook():
|
||||
@hooks.register('dashboard_hook')
|
||||
def register_admin_hook():
|
||||
return AdminHook()
|
||||
|
||||
|
||||
@hooks.register('dashboard_hook')
|
||||
def register_esi_hook():
|
||||
return ESICheckHook()
|
||||
|
||||
@@ -2,7 +2,6 @@ import logging
|
||||
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.contrib.auth.models import User, Permission
|
||||
from django.contrib import messages
|
||||
|
||||
from .models import UserProfile, CharacterOwnership, OwnershipRecord
|
||||
|
||||
@@ -41,9 +40,7 @@ class StateBackend(ModelBackend):
|
||||
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.")
|
||||
else: # this is an alt, enforce main only.
|
||||
return None
|
||||
else:
|
||||
logger.debug(f'{token.character_name} has changed ownership. Creating new user account.')
|
||||
@@ -66,9 +63,7 @@ class StateBackend(ModelBackend):
|
||||
user = records[0].user
|
||||
if user.profile.main_character:
|
||||
if 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.")
|
||||
# this is an alt, enforce main only due to trust issues in SSO.
|
||||
return None
|
||||
|
||||
token.user = user
|
||||
|
||||
12
allianceauth/authentication/constants.py
Normal file
12
allianceauth/authentication/constants.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Overide ESI messages in the dashboard widget
|
||||
# when the returned messages are not helpful or out of date
|
||||
ESI_ERROR_MESSAGE_OVERRIDES = {
|
||||
420: _("This software has exceeded the error limit for ESI. "
|
||||
"If you are a user, please contact the maintainer of this software."
|
||||
" If you are a developer/maintainer, please make a greater "
|
||||
"effort in the future to receive valid responses. For tips on how, "
|
||||
"come have a chat with us in ##3rd-party-dev-and-esi on the EVE "
|
||||
"Online Discord. https://www.eveonline.com/discord")
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.13 on 2024-05-12 09:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0022_userprofile_theme'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='userprofile',
|
||||
name='language',
|
||||
field=models.CharField(blank=True, choices=[('en', 'English'), ('de', 'German'), ('es', 'Spanish'), ('zh-hans', 'Chinese Simplified'), ('ru', 'Russian'), ('ko', 'Korean'), ('fr', 'French'), ('ja', 'Japanese'), ('it', 'Italian'), ('uk', 'Ukrainian'), ('pl', 'Polish')], default='', max_length=10, verbose_name='Language'),
|
||||
),
|
||||
]
|
||||
@@ -78,6 +78,7 @@ class UserProfile(models.Model):
|
||||
JAPANESE = 'ja', _('Japanese')
|
||||
ITALIAN = 'it', _('Italian')
|
||||
UKRAINIAN = 'uk', _('Ukrainian')
|
||||
POLISH = 'pl', _("Polish")
|
||||
|
||||
user = models.OneToOneField(
|
||||
User,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{% extends 'allianceauth/base.html' %}
|
||||
|
||||
|
||||
{% block page_title %}Dashboard{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<h1>Dashboard Dummy</h1>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,13 +1,11 @@
|
||||
{% load i18n %}
|
||||
<div class="col-12 col-xl-8 align-self-stretch p-2 ps-0">
|
||||
<div class="card">
|
||||
<div id="aa-dashboard-panel-characters" class="col-12 col-xl-8 align-self-stretch p-2 ps-0 pe-0 ps-xl-0 pe-xl-2">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<h4 class="ms-auto me-auto">
|
||||
{% translate "Characters" %}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% translate "Characters" as widget_title %}
|
||||
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
|
||||
|
||||
<div>
|
||||
<div style="height: 300px; overflow-y:auto;">
|
||||
<div class="d-flex">
|
||||
<a href="{% url 'authentication:add_character' %}" class="btn btn-primary flex-fill m-1" title="{% translate 'Add Character' %}">
|
||||
@@ -1,11 +1,13 @@
|
||||
{% load i18n %}
|
||||
<div class="col-12 col-xl-4 align-self-stretch py-2 ps-2">
|
||||
<div id="aa-dashboard-panel-membership" class="col-12 col-xl-4 align-self-stretch py-2 ps-xl-2">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title text-center">{% translate "Membership" %}</h4>
|
||||
<div class="card-body">
|
||||
{% translate "Membership" as widget_title %}
|
||||
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
|
||||
|
||||
<div>
|
||||
<div style="height: 300px; overflow-y:auto;">
|
||||
<h6 class="text-center">{% translate "State:" %} {{ request.user.profile.state }}</h6>
|
||||
<h5 class="text-center">{% translate "State:" %} {{ request.user.profile.state }}</h5>
|
||||
<table class="table">
|
||||
{% for group in groups %}
|
||||
<tr>
|
||||
@@ -1,65 +1,85 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "Dashboard" %}{% endblock page_title %}
|
||||
{% block page_title %}
|
||||
{% translate "Token Management" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Token Management" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="page-header text-center">{% translate "Token Management" %}</h1>
|
||||
<div>
|
||||
<table class="table table-aa" id="table_tokens" style="width: 100%;">
|
||||
<p class="mb-3">
|
||||
{% 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 %}
|
||||
</p>
|
||||
|
||||
<table class="table w-100" id="table_tokens">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Scopes" %}</th>
|
||||
<th class="text-end">{% translate "Actions" %}</th>
|
||||
<th>{% translate "Character" %}</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for t in tokens %}
|
||||
<tr>
|
||||
<td style="white-space:initial;">{% for s in t.scopes.all %}<span class="badge bg-secondary">{{ s.name }}</span>{% endfor %}</td>
|
||||
<td nowrap class="text-end">
|
||||
<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 style="white-space:initial;">
|
||||
{% for s in t.scopes.all %}
|
||||
<span class="badge bg-secondary">{{ s.name }}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
|
||||
<td nowrap class="text-end">
|
||||
<a href="{% url 'authentication:token_delete' t.id %}" class="btn btn-danger"><i class="fa-solid fa-trash-can"></i></a>
|
||||
<a href="{% url 'authentication:token_refresh' t.id %}" class="btn btn-success"><i class="fa-solid fa-rotate"></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 content %}
|
||||
|
||||
{% block extra_javascript %}
|
||||
{% include "bundles/datatables-js-bs5.html" %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
let grp = 2;
|
||||
|
||||
const 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((group, i) => {
|
||||
if (last !== group) {
|
||||
$(rows).eq(i).before(`<tr class="h5 table-primary"><td colspan="3">${group}</td></tr>`);
|
||||
|
||||
last = group;
|
||||
}
|
||||
});
|
||||
},
|
||||
'stateSave': true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock extra_javascript %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include "bundles/datatables-css-bs5.html" %}
|
||||
{% endblock extra_css %}
|
||||
|
||||
{% 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 extra_script %}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{% load theme_tags %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -7,7 +8,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<!-- TODO Bundle all the site specific stuff up into its own template for easy overide -->
|
||||
<!-- TODO Bundle all the site specific stuff up into its own template for easy override -->
|
||||
<meta property="og:title" content="{{ SITE_NAME }}">
|
||||
<meta property="og:image" content="{{ SITE_URL }}{% static 'allianceauth/icons/apple-touch-icon.png' %}">
|
||||
<meta property="og:description" content="Alliance Auth - An auth system for EVE Online to help in-game organizations manage online service access.">
|
||||
@@ -16,8 +17,9 @@
|
||||
|
||||
<title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title>
|
||||
|
||||
{% include 'bundles/bootstrap-css.html' %}
|
||||
{% theme_css %}
|
||||
{% include 'bundles/fontawesome.html' %}
|
||||
|
||||
{% block extra_include %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -30,25 +32,23 @@
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.panel-transparent {
|
||||
background: rgba(48 48 48 / 0.7);
|
||||
color: #ffffff;
|
||||
.card-login {
|
||||
background: rgba(48 48 48 / 0.7);
|
||||
color: rgb(255 255 255);
|
||||
padding-bottom: 21px;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
|
||||
}
|
||||
|
||||
#lang-select {
|
||||
width: 40%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
{% block extra_style %}
|
||||
{% endblock %}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container" style="margin-top:150px;">
|
||||
{% block content %}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="dropdown">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<select onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
|
||||
|
||||
<select class="form-select" onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
|
||||
{% for language in languages %}
|
||||
<option lang="{{ language.code }}" value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local|capfirst }} ({{ language.code }})
|
||||
|
||||
@@ -3,39 +3,41 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<div class="panel panel-default panel-transparent">
|
||||
<div class="panel-body">
|
||||
<div class="col-md-12">
|
||||
{% block middle_box_content %}
|
||||
{% endblock %}
|
||||
<div class="card card-login border-secondary p-3">
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
{% block middle_box_content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'public/lang_select.html' %}
|
||||
|
||||
<p class="text-center mt-3">
|
||||
{% translate "For information on SSO, ESI and security read the CCP Dev Blog" %}<br>
|
||||
<a class="text-reset" href="https://www.eveonline.com/news/view/introducing-esi" target="_blank" rel="noopener noreferrer">
|
||||
{% translate "Introducing ESI - A New API For EVE Online" %}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p class="text-center">
|
||||
<a class="text-reset" href="https://community.eveonline.com/support/third-party-applications/" target="_blank" rel="noopener noreferrer">
|
||||
{% translate "Manage ESI Applications" %}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_include %}
|
||||
{% include 'bundles/bootstrap-js.html' %}
|
||||
{% include 'bundles/bootstrap-js-bs5.html' %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
{% extends 'public/base.html' %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load django_bootstrap5 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "Registration" %}{% endblock %}
|
||||
|
||||
{% block extra_include %}
|
||||
{% include 'bundles/bootstrap-css.html' %}
|
||||
{% include 'bundles/bootstrap-css-bs5.html' %}
|
||||
{% include 'bundles/fontawesome.html' %}
|
||||
{% include 'bundles/bootstrap-js.html' %}
|
||||
{% include 'bundles/bootstrap-js-bs5.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="panel panel-default panel-transparent">
|
||||
<div class="panel-body">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{ form|bootstrap }}
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Register" %}</button>
|
||||
</form>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-login border-secondary p-3">
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
<button class="btn btn-primary btn-block" type="submit">{% translate "Register" %}</button>
|
||||
</form>
|
||||
|
||||
{% include 'public/lang_select.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'public/lang_select.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{% extends 'public/middle_box.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block middle_box_content %}
|
||||
<div class="alert alert-danger">{% translate 'Invalid or expired activation link.' %}</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import json
|
||||
import requests_mock
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from allianceauth.authentication.views import task_counts
|
||||
from allianceauth.authentication.views import task_counts, esi_check
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
from allianceauth.authentication.constants import ESI_ERROR_MESSAGE_OVERRIDES
|
||||
|
||||
MODULE_PATH = "allianceauth.authentication.views"
|
||||
|
||||
@@ -21,6 +23,8 @@ class TestRunningTasksCount(TestCase):
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
cls.user = AuthUtils.create_user("bruce_wayne")
|
||||
cls.user.is_superuser = True
|
||||
cls.user.save()
|
||||
|
||||
def test_should_return_data(
|
||||
self, mock_active_tasks_count, mock_queued_tasks_count
|
||||
@@ -35,5 +39,164 @@ class TestRunningTasksCount(TestCase):
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {"tasks_running": 2, "tasks_queued": 3}
|
||||
jsonresponse_to_dict(response), {
|
||||
"tasks_running": 2, "tasks_queued": 3}
|
||||
)
|
||||
|
||||
def test_su_only(
|
||||
self, mock_active_tasks_count, mock_queued_tasks_count
|
||||
):
|
||||
self.user.is_superuser = False
|
||||
self.user.save()
|
||||
self.user.refresh_from_db()
|
||||
# given
|
||||
mock_active_tasks_count.return_value = 2
|
||||
mock_queued_tasks_count.return_value = 3
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = task_counts(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
|
||||
class TestEsiCheck(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
cls.user = AuthUtils.create_user("bruce_wayne")
|
||||
cls.user.is_superuser = True
|
||||
cls.user.save()
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_401_data_returns_200(
|
||||
self, m
|
||||
):
|
||||
error_json = {
|
||||
"error": "You have been banned from using ESI. Please contact Technical Support. (support@eveonline.com)"
|
||||
}
|
||||
status_code = 401
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(error_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": error_json
|
||||
}
|
||||
)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_504_data_returns_200(
|
||||
self, m
|
||||
):
|
||||
error_json = {
|
||||
"error": "Gateway timeout message",
|
||||
"timeout": 5000
|
||||
}
|
||||
status_code = 504
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(error_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": error_json
|
||||
}
|
||||
)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_420_data_override(
|
||||
self, m
|
||||
):
|
||||
error_json = {
|
||||
"error": "message from CCP",
|
||||
}
|
||||
status_code = 420
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(error_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotEqual(
|
||||
jsonresponse_to_dict(response)["data"],
|
||||
error_json
|
||||
)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": {
|
||||
"error": ESI_ERROR_MESSAGE_OVERRIDES.get(status_code)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_200_data_returns_200(
|
||||
self, m
|
||||
):
|
||||
good_json = {
|
||||
"players": 5,
|
||||
"server_version": "69420",
|
||||
"start_time": "2030-01-01T23:59:59Z"
|
||||
}
|
||||
status_code = 200
|
||||
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(good_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": good_json
|
||||
}
|
||||
)
|
||||
|
||||
def test_su_only(
|
||||
self,
|
||||
):
|
||||
self.user.is_superuser = False
|
||||
self.user.save()
|
||||
self.user.refresh_from_db()
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
@@ -38,5 +38,7 @@ urlpatterns = [
|
||||
name='token_refresh'
|
||||
),
|
||||
path('dashboard/', views.dashboard, name='dashboard'),
|
||||
path('dashboard_bs3/', views.dashboard_bs3, name='dashboard_bs3'),
|
||||
path('task-counts/', views.task_counts, name='task_counts'),
|
||||
path('esi-check/', views.esi_check, name='esi_check'),
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from allianceauth.hooks import get_hooks
|
||||
|
||||
import requests
|
||||
from django_registration.backends.activation.views import (
|
||||
REGISTRATION_SALT, ActivationView as BaseActivationView,
|
||||
RegistrationView as BaseRegistrationView,
|
||||
@@ -10,7 +10,7 @@ from django_registration.signals import user_registered
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import authenticate, login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import signing
|
||||
from django.http import JsonResponse
|
||||
@@ -23,14 +23,16 @@ from esi.decorators import token_required
|
||||
from esi.models import Token
|
||||
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
from allianceauth.hooks import get_hooks
|
||||
|
||||
from .constants import ESI_ERROR_MESSAGE_OVERRIDES
|
||||
from .core.celery_workers import active_tasks_count, queued_tasks_count
|
||||
from .forms import RegistrationForm
|
||||
from .models import CharacterOwnership
|
||||
|
||||
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||
_has_auto_groups = True
|
||||
from allianceauth.eveonline.autogroups.models import *
|
||||
from allianceauth.eveonline.autogroups.models import * # noqa: F401, F403
|
||||
else:
|
||||
_has_auto_groups = False
|
||||
|
||||
@@ -54,7 +56,7 @@ def dashboard_groups(request):
|
||||
context = {
|
||||
'groups': groups,
|
||||
}
|
||||
return render_to_string('authentication/dashboard.groups.html', context=context, request=request)
|
||||
return render_to_string('authentication/dashboard_groups.html', context=context, request=request)
|
||||
|
||||
|
||||
def dashboard_characters(request):
|
||||
@@ -66,7 +68,7 @@ def dashboard_characters(request):
|
||||
context = {
|
||||
'characters': characters
|
||||
}
|
||||
return render_to_string('authentication/dashboard.characters.html', context=context, request=request)
|
||||
return render_to_string('authentication/dashboard_characters.html', context=context, request=request)
|
||||
|
||||
|
||||
def dashboard_admin(request):
|
||||
@@ -76,6 +78,13 @@ def dashboard_admin(request):
|
||||
return ""
|
||||
|
||||
|
||||
def dashboard_esi_check(request):
|
||||
if request.user.is_superuser:
|
||||
return render_to_string('allianceauth/admin-status/esi_check.html', request=request)
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
@login_required
|
||||
def dashboard(request):
|
||||
_dash_items = list()
|
||||
@@ -135,23 +144,30 @@ def token_refresh(request, token_id=None):
|
||||
@login_required
|
||||
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
|
||||
def main_character_change(request, token):
|
||||
logger.debug(f"main_character_change called by user {request.user} for character {token.character_name}")
|
||||
logger.debug(
|
||||
f"main_character_change called by user {request.user} for character {token.character_name}")
|
||||
try:
|
||||
co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user)
|
||||
co = CharacterOwnership.objects.get(
|
||||
character__character_id=token.character_id, user=request.user)
|
||||
except CharacterOwnership.DoesNotExist:
|
||||
if not CharacterOwnership.objects.filter(character__character_id=token.character_id).exists():
|
||||
co = CharacterOwnership.objects.create_by_token(token)
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
_('Cannot change main character to %(char)s: character owned by a different account.') % ({'char': token.character_name})
|
||||
_('Cannot change main character to %(char)s: character owned by a different account.') % (
|
||||
{'char': token.character_name})
|
||||
)
|
||||
co = None
|
||||
if co:
|
||||
request.user.profile.main_character = co.character
|
||||
request.user.profile.save(update_fields=['main_character'])
|
||||
messages.success(request, _('Changed main character to %(char)s') % {"char": co.character})
|
||||
logger.info('Changed user %(user)s main character to %(char)s' % ({'user': request.user, 'char': co.character}))
|
||||
messages.success(request, _('Changed main character to %s') % co.character)
|
||||
logger.info(
|
||||
'Changed user {user} main character to {char}'.format(
|
||||
user=request.user, char=co.character
|
||||
)
|
||||
)
|
||||
return redirect("authentication:dashboard")
|
||||
|
||||
|
||||
@@ -159,9 +175,11 @@ def main_character_change(request, token):
|
||||
def add_character(request, token):
|
||||
if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter(
|
||||
owner_hash=token.character_owner_hash).filter(user=request.user).exists():
|
||||
messages.success(request, _('Added %(name)s to your account.' % ({'name': token.character_name})))
|
||||
messages.success(request, _(
|
||||
'Added %(name)s to your account.' % ({'name': token.character_name})))
|
||||
else:
|
||||
messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % ({'name': token.character_name})))
|
||||
messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % (
|
||||
{'name': token.character_name})))
|
||||
return redirect('authentication:dashboard')
|
||||
|
||||
|
||||
@@ -200,7 +218,15 @@ def sso_login(request, token):
|
||||
request.session['registration_uid'] = user.pk
|
||||
# Go to Step 2
|
||||
return redirect('registration_register')
|
||||
messages.error(request, _('Unable to authenticate as the selected character.'))
|
||||
# Logging in with an alt is not allowed due to security concerns.
|
||||
token.delete()
|
||||
messages.error(
|
||||
request,
|
||||
_(
|
||||
'Unable to authenticate as the selected character. '
|
||||
'Please log in with the main character associated with this account.'
|
||||
)
|
||||
)
|
||||
return redirect(settings.LOGIN_URL)
|
||||
|
||||
|
||||
@@ -272,7 +298,8 @@ class RegistrationView(BaseRegistrationView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def register(self, form):
|
||||
user = User.objects.get(pk=self.request.session.get('registration_uid'))
|
||||
user = User.objects.get(
|
||||
pk=self.request.session.get('registration_uid'))
|
||||
user.email = form.cleaned_data['email']
|
||||
user_registered.send(self.__class__, user=user, request=self.request)
|
||||
if getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
|
||||
@@ -289,7 +316,8 @@ class RegistrationView(BaseRegistrationView):
|
||||
|
||||
def get_email_context(self, activation_key):
|
||||
context = super().get_email_context(activation_key)
|
||||
context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key])
|
||||
context['url'] = context['site'].domain + \
|
||||
reverse('registration_activate', args=[activation_key])
|
||||
return context
|
||||
|
||||
|
||||
@@ -322,20 +350,24 @@ class ActivationView(BaseActivationView):
|
||||
|
||||
|
||||
def registration_complete(request):
|
||||
messages.success(request, _('Sent confirmation email. Please follow the link to confirm your email address.'))
|
||||
messages.success(request, _(
|
||||
'Sent confirmation email. Please follow the link to confirm your email address.'))
|
||||
return redirect('authentication:login')
|
||||
|
||||
|
||||
def activation_complete(request):
|
||||
messages.success(request, _('Confirmed your email address. Please login to continue.'))
|
||||
messages.success(request, _(
|
||||
'Confirmed your email address. Please login to continue.'))
|
||||
return redirect('authentication:dashboard')
|
||||
|
||||
|
||||
def registration_closed(request):
|
||||
messages.error(request, _('Registration of new accounts is not allowed at this time.'))
|
||||
messages.error(request, _(
|
||||
'Registration of new accounts is not allowed at this time.'))
|
||||
return redirect('authentication:login')
|
||||
|
||||
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def task_counts(request) -> JsonResponse:
|
||||
"""Return task counts as JSON for an AJAX call."""
|
||||
data = {
|
||||
@@ -343,3 +375,31 @@ def task_counts(request) -> JsonResponse:
|
||||
"tasks_queued": queued_tasks_count()
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
def check_for_override_esi_error_message(response):
|
||||
if response.status_code in ESI_ERROR_MESSAGE_OVERRIDES:
|
||||
return {"error": ESI_ERROR_MESSAGE_OVERRIDES.get(response.status_code)}
|
||||
else:
|
||||
return response.json()
|
||||
|
||||
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def esi_check(request) -> JsonResponse:
|
||||
"""Return if ESI ok With error messages and codes as JSON"""
|
||||
_r = requests.get("https://esi.evetech.net/latest/status/?datasource=tranquility")
|
||||
|
||||
data = {
|
||||
"status": _r.status_code,
|
||||
"data": check_for_override_esi_error_message(_r)
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@login_required
|
||||
def dashboard_bs3(request):
|
||||
"""Render dashboard view with BS3 theme.
|
||||
|
||||
This is an internal view used for testing BS3 backward compatibility in AA4 only.
|
||||
"""
|
||||
return render(request, 'authentication/dashboard_bs3.html')
|
||||
|
||||
@@ -12,13 +12,14 @@ class StartProject(BaseStartProject):
|
||||
parser.add_argument('--python', help='The path to the python executable.')
|
||||
parser.add_argument('--celery', help='The path to the celery executable.')
|
||||
parser.add_argument('--gunicorn', help='The path to the gunicorn executable.')
|
||||
parser.add_argument('--memmon', help='The path to the memmon executable.')
|
||||
|
||||
|
||||
def create_project(parser, options, args):
|
||||
# Validate args
|
||||
if len(args) < 2:
|
||||
parser.error("Please specify a name for your Alliance Auth installation.")
|
||||
elif len(args) > 3:
|
||||
elif len(args) > 4:
|
||||
parser.error("Too many arguments.")
|
||||
|
||||
# First find the path to Alliance Auth
|
||||
|
||||
@@ -10,7 +10,7 @@ class CorpStats(MenuItemHook):
|
||||
MenuItemHook.__init__(
|
||||
self,
|
||||
_('Corporation Stats'),
|
||||
'fas fa-share-alt fa-fw',
|
||||
'fa-solid fa-share-nodes',
|
||||
'corputils:view',
|
||||
navactive=['corputils:']
|
||||
)
|
||||
|
||||
@@ -1,39 +1,62 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Corporation Member Data" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Corporation Member Data" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block header_nav_collapse_left %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">
|
||||
{% translate "Corporations" %}
|
||||
</a>
|
||||
|
||||
<ul class="dropdown-menu">
|
||||
{% for corpstat in available %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'corputils:view_corp' corpstat.corp.corporation_id %}">
|
||||
{{ corpstat.corp.corporation_name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
{% if perms.corputils.add_corpstats %}
|
||||
{% if available.count >= 1 %}
|
||||
<li> </li>
|
||||
{% endif %}
|
||||
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'corputils:add' %}">
|
||||
{% translate "Add corporation" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_nav_collapse_right %}
|
||||
<li class="nav-item">
|
||||
<form class="navbar-form navbar-right" role="search" action="{% url 'corputils:search' %}" method="GET">
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="search_string"
|
||||
placeholder="{% if search_string %}{{ search_string }}{% else %}{% translate 'Search all corporations...' %}{% endif %}"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<h1 class="page-header text-center">{% translate "Corporation Member Data" %}</h1>
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" id="dLabel" class="dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">{% translate "Corporations" %}<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
|
||||
{% for corpstat in available %}
|
||||
<li>
|
||||
<a href="{% url 'corputils:view_corp' corpstat.corp.corporation_id %}">{{ corpstat.corp.corporation_name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% if perms.corputils.add_corpstats %}
|
||||
<li>
|
||||
<a href="{% url 'corputils:add' %}">{% translate "Add" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<form class="navbar-form navbar-right" role="search" action="{% url 'corputils:search' %}" method="GET">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="search_string" placeholder="{% if search_string %}{{ search_string }}{% else %}{% translate 'Search all corporations...' %}{% endif %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{% block member_data %}
|
||||
{% endblock member_data %}
|
||||
</div>
|
||||
|
||||
@@ -1,178 +1,233 @@
|
||||
{% extends 'corputils/base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
|
||||
{% block member_data %}
|
||||
{% if corpstats %}
|
||||
<div>
|
||||
<table class="table">
|
||||
<table class="table text-center">
|
||||
<tr>
|
||||
<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 }}" alt="{{ corpstats.corp.corporation_name }}">
|
||||
</td>
|
||||
<td>
|
||||
<img class="ra-avatar" src="{{ corpstats.corp.logo_url_64 }}" alt="{{ corpstats.corp.corporation_name }}">
|
||||
</td>
|
||||
|
||||
{% if corpstats.corp.alliance %}
|
||||
<td class="text-center col-lg-6">
|
||||
<td>
|
||||
<img class="ra-avatar" src="{{ corpstats.corp.alliance.logo_url_64 }}" alt="{{ corpstats.corp.alliance.alliance_name }}">
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="text-center"><h4>{{ corpstats.corp.corporation_name }}</h4></td>
|
||||
<td><p class="h4">{{ corpstats.corp.corporation_name }}</p></td>
|
||||
|
||||
{% if corpstats.corp.alliance %}
|
||||
<td class="text-center"><h4>{{ corpstats.corp.alliance.alliance_name }}</h4></td>
|
||||
<td><p class="h4">{{ corpstats.corp.alliance.alliance_name }}</p></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<ul class="nav nav-pills pull-left">
|
||||
<li class="active"><a href="#mains" data-toggle="pill">{% translate 'Mains' %} ({{ total_mains }})</a></li>
|
||||
<li><a href="#members" data-toggle="pill">{% translate 'Members' %} ({{ corpstats.member_count }})</a></li>
|
||||
<li><a href="#unregistered" data-toggle="pill">{% translate 'Unregistered' %} ({{ unregistered.count }})</a></li>
|
||||
</ul>
|
||||
<div class="pull-right hidden-xs">
|
||||
{% translate "Last update:" %} {{ corpstats.last_update|naturaltime }}
|
||||
<a class="btn btn-success" type="button" href="{% url 'corputils:update' corpstats.corp.corporation_id %}" title="Update Now">
|
||||
<span class="glyphicon glyphicon-refresh"></span>
|
||||
<div class="card card-default mt-4">
|
||||
<div class="card-header clearfix" role="tablist">
|
||||
<ul class="nav nav-pills float-start">
|
||||
<li class="nav-item" role="presentation">
|
||||
<a
|
||||
class="nav-link active"
|
||||
id="mains"
|
||||
data-bs-toggle="tab"
|
||||
href="#tab-mains"
|
||||
role="tab"
|
||||
aria-controls="tab-mains"
|
||||
aria-selected="true"
|
||||
>
|
||||
{% translate 'Mains' %} ({{ total_mains }})
|
||||
</a>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade in active" id="mains">
|
||||
{% if mains %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="table-mains">
|
||||
<thead>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a
|
||||
class="nav-link"
|
||||
id="members"
|
||||
data-bs-toggle="tab"
|
||||
href="#tab-members"
|
||||
role="tab"
|
||||
aria-controls="tab-members"
|
||||
aria-selected="false"
|
||||
>
|
||||
{% translate 'Members' %} ({{ corpstats.member_count }})
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link"
|
||||
id="unregistered"
|
||||
data-bs-toggle="tab"
|
||||
href="#tab-unregistered"
|
||||
role="tab"
|
||||
aria-controls="tab-unregistered"
|
||||
aria-selected="false"
|
||||
>
|
||||
{% translate 'Unregistered' %} ({{ unregistered.count }})
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="float-end d-none d-sm-block">
|
||||
{% translate "Last update:" %} {{ corpstats.last_update|naturaltime }}
|
||||
|
||||
<a
|
||||
class="btn btn-success btn-sm ms-2"
|
||||
type="button"
|
||||
href="{% url 'corputils:update' corpstats.corp.corporation_id %}"
|
||||
title="{% translate 'Update Now' %}"
|
||||
>
|
||||
<i class="fa-solid fa-rotate"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="tab-mains" role="tabpanel" aria-labelledby="tab-mains">
|
||||
{% if mains %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="table-mains">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Main character" %}</th>
|
||||
<th>{% translate "Registered characters" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for id, main in mains.items %}
|
||||
<tr>
|
||||
<th style="height:1em;"><!-- Must have text or height to prevent clipping --></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for id, main in mains.items %}
|
||||
<tr>
|
||||
<td class="text-center" style="vertical-align:middle">
|
||||
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||
<img src="{{ main.main.portrait_url_64 }}" class="img-circle" alt="{{ main.main }}">
|
||||
<div class="caption text-center">
|
||||
{{ main.main }}
|
||||
</div>
|
||||
<td class="text-center" style="vertical-align: middle;">
|
||||
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||
<img src="{{ main.main.portrait_url_64 }}" class="img-circle" alt="{{ main.main }}">
|
||||
<div class="caption">
|
||||
{{ main.main }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<table class="table table-hover">
|
||||
{% for alt in main.alts %}
|
||||
{% if forloop.first %}
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-center">{% translate "Character" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Alliance" %}</th>
|
||||
<th class="text-center"></th>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<table class="table table-hover">
|
||||
{% for alt in main.alts|dictsort:"character_name" %}
|
||||
{% if forloop.first %}
|
||||
<tr>
|
||||
<td class="text-center" style="width:5%">
|
||||
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||
<img src="{{ alt.portrait_url_32 }}" class="img-circle" alt="{{ alt.character_name }}">
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center" style="width:30%">{{ alt.character_name }}</td>
|
||||
<td class="text-center" style="width:30%">{{ alt.corporation_name }}</td>
|
||||
<td class="text-center" style="width:30%">{{ alt.alliance_name }}</td>
|
||||
<td class="text-center" style="width:5%">
|
||||
<a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="badge bg-danger" target="_blank">
|
||||
{% translate "Killboard" %}
|
||||
</a>
|
||||
</td>
|
||||
<th></th>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th>{% translate "Corporation" %}</th>
|
||||
<th>{% translate "Alliance" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tab-pane fade" id="members">
|
||||
{% if members %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="table-members">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-center">{% translate "Character" %}</th>
|
||||
<th class="text-center"></th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
<th class="text-center">{% translate "Main Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Main Alliance" %}</th>
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<td style="width: 5%;">
|
||||
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
|
||||
<img src="{{ alt.portrait_url_32 }}" class="img-circle" alt="{{ alt.character_name }}">
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 30%;">{{ alt.character_name }}</td>
|
||||
<td style="width: 30%;">{{ alt.corporation_name }}</td>
|
||||
<td style="width: 30%;">{{ alt.alliance_name|default_if_none:"" }}</td>
|
||||
<td style="width: 5%;">
|
||||
<a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="badge bg-danger" target="_blank">
|
||||
{% translate "Killboard" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in members %}
|
||||
<tr>
|
||||
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member }}"></td>
|
||||
<td class="text-center">{{ member }}</td>
|
||||
<td class="text-center">
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a>
|
||||
</td>
|
||||
<td class="text-center">{{ member.character_ownership.user.profile.main_character.character_name }}</td>
|
||||
<td class="text-center">{{ member.character_ownership.user.profile.main_character.corporation_name }}</td>
|
||||
<td class="text-center">{{ member.character_ownership.user.profile.main_character.alliance_name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for member in unregistered %}
|
||||
<tr class="danger">
|
||||
<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">
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a>
|
||||
</td>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tab-pane fade" id="unregistered">
|
||||
{% if unregistered %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="table-unregistered">
|
||||
<thead>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="tab-members" role="tabpanel" aria-labelledby="tab-members">
|
||||
{% if members %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="table-members">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th></th>
|
||||
<th>{% translate "Main Character" %}</th>
|
||||
<th>{% translate "Main Corporation" %}</th>
|
||||
<th>{% translate "Main Alliance" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for member in members %}
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-center">{% translate "Character" %}</th>
|
||||
<th class="text-center"></th>
|
||||
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member }}"></td>
|
||||
<td>{{ member }}</td>
|
||||
<td>
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a>
|
||||
</td>
|
||||
<td>{{ member.character_ownership.user.profile.main_character.character_name }}</td>
|
||||
<td>{{ member.character_ownership.user.profile.main_character.corporation_name }}</td>
|
||||
<td>{{ member.character_ownership.user.profile.main_character.alliance_name|default_if_none:"" }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in unregistered %}
|
||||
<tr class="danger">
|
||||
<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">
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">
|
||||
{% translate "Killboard" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for member in unregistered %}
|
||||
<tr class="table-danger">
|
||||
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td>
|
||||
<td>{{ member.character_name }}</td>
|
||||
<td>
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="tab-unregistered" role="tabpanel" aria-labelledby="tab-unregistered">
|
||||
{% if unregistered %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="table-unregistered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for member in unregistered %}
|
||||
<tr class="table-danger">
|
||||
<td><img src="{{ member.portrait_url }}" class="img-circle" alt="{{ member.character_name }}"></td>
|
||||
<td>{{ member.character_name }}</td>
|
||||
<td>
|
||||
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="badge bg-danger" target="_blank">
|
||||
{% translate "Killboard" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -181,40 +236,41 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_javascript %}
|
||||
{% include 'bundles/datatables-js.html' %}
|
||||
{% include 'bundles/datatables-js-bs5.html' %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
$('#table-mains').DataTable({
|
||||
"columnDefs": [
|
||||
{ "sortable": false, "targets": [1] },
|
||||
],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
|
||||
$('#table-members').DataTable({
|
||||
"columnDefs": [
|
||||
{ "searchable": false, "targets": [0, 2] },
|
||||
{ "sortable": false, "targets": [0, 2] },
|
||||
],
|
||||
"order": [[ 1, "asc" ]],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
|
||||
$('#table-unregistered').DataTable({
|
||||
"columnDefs": [
|
||||
{ "searchable": false, "targets": [0, 2] },
|
||||
{ "sortable": false, "targets": [0, 2] },
|
||||
],
|
||||
"order": [[ 1, "asc" ]],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'bundles/datatables-css.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
$(document).ready(function(){
|
||||
$('#table-mains').DataTable({
|
||||
"columnDefs": [
|
||||
{ "sortable": false, "targets": [1] },
|
||||
],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
$('#table-members').DataTable({
|
||||
"columnDefs": [
|
||||
{ "searchable": false, "targets": [0, 2] },
|
||||
{ "sortable": false, "targets": [0, 2] },
|
||||
],
|
||||
"order": [[ 1, "asc" ]],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
$('#table-unregistered').DataTable({
|
||||
"columnDefs": [
|
||||
{ "searchable": false, "targets": [0, 2] },
|
||||
{ "sortable": false, "targets": [0, 2] },
|
||||
],
|
||||
"order": [[ 1, "asc" ]],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
|
||||
});
|
||||
{% include 'bundles/datatables-css-bs5.html' %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
{% extends "corputils/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block member_data %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading clearfix">
|
||||
<div class="panel-title pull-left">{% translate "Search Results" %}</div>
|
||||
<div class="card card-default">
|
||||
<div class="card-header clearfix">
|
||||
<div class="card-title">{% translate "Search Results" %}</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="card-body mt-2">
|
||||
<table class="table table-hover" id="table-search">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"></th>
|
||||
<th class="text-center">{% translate "Character" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}</th>
|
||||
<th class="text-center">{% translate "zKillboard" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
<th class="text-center">{% translate "Main Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Main Alliance" %}</th>
|
||||
<th></th>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th>{% translate "Corporation" %}</th>
|
||||
<th>{% translate "zKillboard" %}</th>
|
||||
<th>{% translate "Main Character" %}</th>
|
||||
<th>{% translate "Main Corporation" %}</th>
|
||||
<th>{% translate "Main Alliance" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for result in results %}
|
||||
<tr {% if not result.1.registered %}class="danger"{% endif %}>
|
||||
<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.0.corp.corporation_name }}</td>
|
||||
<td class="text-center"><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a></td>
|
||||
<td class="text-center">{{ result.1.main_character.character_name }}</td>
|
||||
<td class="text-center">{{ result.1.main_character.corporation_name }}</td>
|
||||
<td class="text-center">{{ result.1.main_character.alliance_name }}</td>
|
||||
<td><img src="{{ result.1.portrait_url }}" class="img-circle" alt="{{ result.1.character_name }}"></td>
|
||||
<td>{{ result.1.character_name }}</td>
|
||||
<td >{{ result.0.corp.corporation_name }}</td>
|
||||
<td><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="badge bg-danger" target="_blank">{% translate "Killboard" %}</a></td>
|
||||
<td>{{ result.1.main_character.character_name }}</td>
|
||||
<td>{{ result.1.main_character.corporation_name }}</td>
|
||||
<td>{{ result.1.main_character.alliance_name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -35,17 +38,20 @@
|
||||
</div>
|
||||
</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(){
|
||||
$('#table-search').DataTable({
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
{% include 'bundles/datatables-js-bs5.html' %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
$('#table-search').DataTable({
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'bundles/datatables-css-bs5.html' %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -14,15 +14,13 @@ Needs to be called with a context containing three objects:
|
||||
{% block page_title %}Evelinks Examples{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
{% include "framework/header/page-header.html" with title="Evelinks templatetags examples" %}
|
||||
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">Evelinks templatetags examples</h1>
|
||||
<div class="col-lg-12 container">
|
||||
|
||||
<h2>profile URLs</h2>
|
||||
|
||||
<div class="rows">
|
||||
|
||||
<div class="col-md-4">
|
||||
<h3>evewho</h3>
|
||||
<p><a href="{{ my_character|evewho_character_url }}">character from character object</a></p>
|
||||
@@ -57,7 +55,6 @@ Needs to be called with a context containing three objects:
|
||||
<h2>image URLs</h2>
|
||||
|
||||
<div class="rows">
|
||||
|
||||
<div class="col-md-4">
|
||||
<p>character from ID: <img src="{{ my_character.character_id|character_portrait_url:128 }}"></p>
|
||||
<p>character from character object: <img src="{{ my_character|character_portrait_url:128 }}"></p>
|
||||
@@ -77,5 +74,4 @@ Needs to be called with a context containing three objects:
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@@ -7,7 +7,7 @@ from allianceauth.services.hooks import UrlHook
|
||||
|
||||
@hooks.register('menu_item_hook')
|
||||
def register_menu():
|
||||
return MenuItemHook(_('Fleet Activity Tracking'), 'fas fa-users fa-fw', 'fatlink:view',
|
||||
return MenuItemHook(_('Fleet Activity Tracking'), 'fa-solid fa-users', 'fatlink:view',
|
||||
navactive=['fatlink:'])
|
||||
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class FatlinkForm(forms.Form):
|
||||
fleet = forms.CharField(label=_("Fleet Name"), max_length=50)
|
||||
duration = forms.IntegerField(label=_("Duration of fat-link"), required=True, initial=30, min_value=1, max_value=2147483647, help_text=_('minutes'))
|
||||
duration = forms.IntegerField(label=_("Duration of fat-link"), required=True, initial=30, min_value=1, max_value=2147483647, help_text=_('Duration of the fat-link in minutes'))
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Migration to AA Framework API method
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
import allianceauth.framework.api.user
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("fleetactivitytracking", "0006_auto_20180803_0430"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="fatlink",
|
||||
name="creator",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.SET(allianceauth.framework.api.user.get_sentinel_user),
|
||||
to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -3,10 +3,7 @@ from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
|
||||
|
||||
def get_sentinel_user():
|
||||
return User.objects.get_or_create(username='deleted')[0]
|
||||
from allianceauth.framework.api.user import get_sentinel_user
|
||||
|
||||
|
||||
class Fatlink(models.Model):
|
||||
|
||||
@@ -1,23 +1,43 @@
|
||||
{% extends 'allianceauth/base-bs5.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Fleet Participation" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "Character not found!" %}</h1>
|
||||
<div class="col-lg-12 container" id="example">
|
||||
<div>
|
||||
{% translate "Character not found!" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
<div class="col-lg-12 container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{{ character_name }}</div>
|
||||
<div class="panel-body">
|
||||
<div class="card card-default">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">
|
||||
{{ character_name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="col-lg-2 col-sm-2">
|
||||
<img class="ra-avatar img-responsive" src="{{ character_portrait_url }}" alt="{{ character_name }}">
|
||||
</div>
|
||||
|
||||
<div class="col-lg-10 col-sm-2">
|
||||
<div class="alert alert-danger" role="alert">{% translate "Character not registered!" %}</div>
|
||||
{% translate "This character is not associated with an auth account." %} <a href=" {% url 'authentication:add_character' %}">{% translate "Add it here" %}</a> {% translate "before attempting to click fleet attendance links." %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{% translate "Character not registered!" %}
|
||||
</div>
|
||||
|
||||
{% translate "This character is not associated with an auth account." %}
|
||||
<a href="{% url 'authentication:add_character' %}">{% translate "Add it here" %}</a>
|
||||
{% translate "before attempting to click fleet attendance links." %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Create Fatlink" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
{% translate "Create Fatlink" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
<div>
|
||||
{% if badrequest %}
|
||||
<div class="alert alert-danger" role="alert">{% translate "Bad request!" %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% for message in errormessages %}
|
||||
<div class="alert alert-danger" role="alert">{{ message }}</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="card card-primary border-0">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">
|
||||
{% translate "Fatlink details" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<form role="form" action="" method="POST">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
<div class="form-group mt-3 clearfix">
|
||||
{% translate "Create fatlink" as button_text %}
|
||||
{% bootstrap_button button_class="btn btn-primary" content=button_text name="submit_fat" id="submit_fat" %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -1,31 +0,0 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
{% block page_title %}
|
||||
{% translate "Create Fatlink" %}
|
||||
{% endblock page_title %}
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "Create Fleet Operation" %}</h1>
|
||||
<div class="container-fluid">
|
||||
{% if badrequest %}
|
||||
<div class="alert alert-danger" role="alert">{% translate "Bad request!" %}</div>
|
||||
{% endif %}
|
||||
{% for message in errormessages %}<div class="alert alert-danger" role="alert">{{ message }}</div>{% endfor %}
|
||||
<div class="col-md-4 offset-md-4">
|
||||
<div class="row">
|
||||
<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"
|
||||
name="submit_fat">
|
||||
{% translate "Create fatlink" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -1,21 +1,35 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% block page_title %}{% translate "Fatlink view" %}{% endblock page_title %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Fatlink view" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "Edit fatlink" %} "{{ fatlink }}"
|
||||
<div class="text-end">
|
||||
<form>
|
||||
<button type="submit" onclick="return confirm('Are you sure?')" class="btn btn-danger" name="deletefat" value="True">
|
||||
{% translate "Delete fat" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="page-header text-center mb-3">
|
||||
{% translate "Edit fatlink" %} "{{ fatlink }}"
|
||||
</h1>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{% translate "Registered characters" %}</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="text-end mb-3">
|
||||
<form>
|
||||
<button type="submit" onclick="return confirm('{% translate "Are you sure?" %}')" class="btn btn-danger" name="deletefat" value="True">
|
||||
{% translate "Delete fat" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card card-default">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">{% translate "Registered characters" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table table-responsive table-hover">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "User" %}</th>
|
||||
@@ -25,21 +39,23 @@
|
||||
<th class="text-center">{% translate "Eve Time" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
{% for fat in registered_fats %}
|
||||
<tr>
|
||||
<td class="text-center">{{ fat.user }}</td>
|
||||
<td class="text-center">{{ fat.character.character_name }}</td>
|
||||
{% if fat.station != "No Station" %}
|
||||
<td class="text-center">{% blocktranslate %}Docked in {% endblocktranslate %}{{ fat.system }}</td>
|
||||
{% else %}
|
||||
<td class="text-center">{{ fat.system }}</td>
|
||||
{% endif %}
|
||||
<td class="text-center">
|
||||
{% if fat.station != "No Station" %}
|
||||
{% translate "Docked in" %}
|
||||
{% endif %}
|
||||
{{ fat.system }}
|
||||
</td>
|
||||
<td class="text-center">{{ fat.shiptype }}</td>
|
||||
<td class="text-center">{{ fat.fatlink.fatdatetime }}</td>
|
||||
<td class="text-center">
|
||||
<form>
|
||||
<button type="submit" class="btn btn-warning" name="removechar" value="{{ fat.character.character_id }}">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
<i class="fa-solid fa-trash-can fa-fw"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
|
||||
@@ -1,71 +1,104 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "Personal fatlink statistics" %}{% endblock page_title %}
|
||||
{% block page_title %}
|
||||
{% translate "Personal fatlink statistics" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
|
||||
{% if char_id %}
|
||||
<div class="text-end">
|
||||
<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 next_month|date:'Y' next_month|date:'m' %}" class="btn btn-info">{% translate "Next month" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<h1 class="page-header text-center mb-3">
|
||||
{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
|
||||
</h1>
|
||||
<h2>
|
||||
{% blocktranslate count links=n_fats trimmed %}
|
||||
{{ user }} has collected one link this month.
|
||||
{% plural %}
|
||||
{{ user }} has collected {{ links }} links this month.
|
||||
{% endblocktranslate %}
|
||||
</h2>
|
||||
<table class="table table-responsive">
|
||||
<tr>
|
||||
<th class="col-md-2 text-center">{% translate "Ship" %}</th>
|
||||
<th class="col-md-2 text-center">{% translate "Times used" %}</th>
|
||||
</tr>
|
||||
{% for ship, n_fats in shipStats %}
|
||||
<tr>
|
||||
<td class="text-center">{{ ship }}</td>
|
||||
<td class="text-center">{{ n_fats }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% if created_fats %}
|
||||
<h2>
|
||||
{% blocktranslate count links=n_created_fats trimmed %}
|
||||
{{ user }} has created one link this month.
|
||||
{% plural %}
|
||||
{{ user }} has created {{ links }} links this month.
|
||||
{% endblocktranslate %}
|
||||
</h2>
|
||||
{% if created_fats %}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Fleet" %}</th>
|
||||
<th class="text-center">{% translate "Creator" %}</th>
|
||||
<th class="text-center">{% translate "Eve Time" %}</th>
|
||||
<th class="text-center">{% translate "Duration" %}</th>
|
||||
<th class="text-center">{% translate "Edit" %}</th>
|
||||
</tr>
|
||||
{% for link in created_fats %}
|
||||
<tr>
|
||||
<td class="text-center"><a href="{% url 'fatlink:click' link.hash %}" class="badge bg-primary">{{ link.fleet }}</a></td>
|
||||
<td class="text-center">{{ link.creator.username }}</td>
|
||||
<td class="text-center">{{ link.fatdatetime }}</td>
|
||||
<td class="text-center">{{ link.duration }}</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'fatlink:modify' link.hash %}">
|
||||
<button type="button" class="btn btn-info"><span
|
||||
class="glyphicon glyphicon-edit"></span></button>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{% if char_id %}
|
||||
<div class="text-end mb-3">
|
||||
<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 next_month|date:'Y' next_month|date:'m' %}" class="btn btn-info">
|
||||
{% translate "Next month" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">
|
||||
{% blocktranslate count links=n_fats trimmed %}
|
||||
{{ user }} has collected one link this month.
|
||||
{% plural %}
|
||||
{{ user }} has collected {{ links }} links this month.
|
||||
{% endblocktranslate %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table table-responsive">
|
||||
<tr>
|
||||
<th class="col-md-2 text-center">{% translate "Ship" %}</th>
|
||||
<th class="col-md-2 text-center">{% translate "Times used" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for ship, n_fats in shipStats %}
|
||||
<tr>
|
||||
<td class="text-center">{{ ship }}</td>
|
||||
<td class="text-center">{{ n_fats }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if created_fats %}
|
||||
<div class="card card-default">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">
|
||||
{% blocktranslate count links=n_created_fats trimmed %}
|
||||
{{ user }} has created one link this month.
|
||||
{% plural %}
|
||||
{{ user }} has created {{ links }} links this month.
|
||||
{% endblocktranslate %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Fleet" %}</th>
|
||||
<th class="text-center">{% translate "Creator" %}</th>
|
||||
<th class="text-center">{% translate "Eve Time" %}</th>
|
||||
<th class="text-center">{% translate "Duration" %}</th>
|
||||
<th class="text-center">{% translate "Edit" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for link in created_fats %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'fatlink:click' link.hash %}" class="badge bg-primary">
|
||||
{{ link.fleet }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">{{ link.creator.username }}</td>
|
||||
<td class="text-center">{{ link.fatdatetime }}</td>
|
||||
<td class="text-center">{{ link.duration }}</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'fatlink:modify' link.hash %}">
|
||||
<button type="button" class="btn btn-info">
|
||||
<i class="fa-solid fa-pen-to-square fa-fw"></i>
|
||||
</button>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Personal fatlink statistics" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">
|
||||
<div>
|
||||
<h1 class="page-header text-center mb-3">
|
||||
{% blocktranslate %}Participation data statistics for {{ year }}{% endblocktranslate %}
|
||||
<div class="text-end">
|
||||
<a href="{% url "fatlink:personal_statistics_year" previous_year %}" class="btn btn-info"><i class="fa-solid fa-chevron-left"></i> {% translate "Previous year" %}</a>
|
||||
{% if next_year %}
|
||||
<a href="{% url "fatlink:personal_statistics_year" next_year %}" class="btn btn-info">{% translate "Next year" %} <i class="fa-solid fa-chevron-right"></i></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<div class="text-end mb-3">
|
||||
<a href="{% url "fatlink:personal_statistics_year" previous_year %}" class="btn btn-info"><i class="fa-solid fa-chevron-left"></i> {% translate "Previous year" %}</a>
|
||||
|
||||
{% if next_year %}
|
||||
<a href="{% url "fatlink:personal_statistics_year" next_year %}" class="btn btn-info">{% translate "Next year" %} <i class="fa-solid fa-chevron-right"></i></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2 offset-lg-5">
|
||||
<table class="table table-responsive">
|
||||
<tr>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Month" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Fats" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for monthnr, month, n_fats in monthlystats %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
|
||||
@@ -1,50 +1,63 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Fatlink Corp Statistics" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">
|
||||
<div>
|
||||
<h1 class="page-header text-center mb-3">
|
||||
{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
|
||||
<div class="text-end">
|
||||
<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 %}
|
||||
<a href="{% url "fatlink:statistics_corp_month" corpid next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</h1>
|
||||
{% if fatStats %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th scope="col" class="col-md-1"></th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Main Character" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Characters" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Fats" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">
|
||||
{% translate "Average fats" %}
|
||||
<i class="fa-solid fa-question" rel="tooltip" title="Fats / Characters"></i>
|
||||
</th>
|
||||
</tr>
|
||||
{% for memberStat in fatStats %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ memberStat.mainchar.portrait_url_32 }}" class="ra-avatar img-responsive" alt="{{ 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_fats }}</td>
|
||||
<td class="text-center">{{ memberStat.avg_fat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<div class="text-end mb-3">
|
||||
<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 %}
|
||||
<a href="{% url "fatlink:statistics_corp_month" corpid next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if fatStats %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th scope="col" class="col-md-1"></th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Main Character" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Characters" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Fats" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">
|
||||
{% translate "Average fats" %}
|
||||
<i class="fa-solid fa-question" rel="tooltip" title="Fats / Characters"></i>
|
||||
</th>
|
||||
</tr>
|
||||
{% for memberStat in fatStats %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ memberStat.mainchar.portrait_url_32 }}" class="ra-avatar img-responsive" alt="{{ 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_fats }}</td>
|
||||
<td class="text-center">{{ memberStat.avg_fat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
{% block extra_script %}
|
||||
$(document).ready(function () {
|
||||
$("[rel=tooltip]").tooltip();
|
||||
});
|
||||
{% endblock extra_script %}
|
||||
|
||||
{% block extra_javascript %}
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
$("[rel=tooltip]").tooltip();
|
||||
});
|
||||
</script>
|
||||
{% endblock extra_javascript %}
|
||||
|
||||
@@ -1,54 +1,67 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Fatlink Statistics" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">
|
||||
<div>
|
||||
<h1 class="page-header text-center mb-3">
|
||||
{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
|
||||
<div class="text-end">
|
||||
<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 %}
|
||||
<a href="{% url 'fatlink:statistics_month' next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</h1>
|
||||
{% if fatStats %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th scope="col" class="col-md-1"></th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Ticker" %}</th>
|
||||
<th scope="col" class="col-md-5 text-center">{% translate "Corp" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Members" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Fats" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">
|
||||
{% translate "Average fats" %}
|
||||
<i class="fa-solid fa-question" rel="tooltip" title="Fats / Characters"></i>
|
||||
</th>
|
||||
</tr>
|
||||
{% for corpStat in fatStats %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ corpStat.corp.logo_url_32 }}" class="ra-avatar img-responsive" alt="{{ corpStat.corp.corporation_name }}">
|
||||
</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.member_count }}</td>
|
||||
<td class="text-center">{{ corpStat.n_fats }}</td>
|
||||
<td class="text-center">{{ corpStat.avg_fat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<div class="text-end mb-3">
|
||||
<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 %}
|
||||
<a href="{% url 'fatlink:statistics_month' next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if fatStats %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th scope="col" class="col-md-1"></th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Ticker" %}</th>
|
||||
<th scope="col" class="col-md-5 text-center">{% translate "Corp" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Members" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">{% translate "Fats" %}</th>
|
||||
<th scope="col" class="col-md-2 text-center">
|
||||
{% translate "Average fats" %}
|
||||
<i class="fa-solid fa-question" rel="tooltip" title="Fats / Characters"></i>
|
||||
</th>
|
||||
</tr>
|
||||
{% for corpStat in fatStats %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ corpStat.corp.logo_url_32 }}" class="ra-avatar img-responsive" alt="{{ corpStat.corp.corporation_name }}">
|
||||
</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.member_count }}</td>
|
||||
<td class="text-center">{{ corpStat.n_fats }}</td>
|
||||
<td class="text-center">{{ corpStat.avg_fat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
{% block extra_script %}
|
||||
$(document).ready(function () {
|
||||
$("[rel=tooltip]").tooltip();
|
||||
});
|
||||
{% endblock extra_script %}
|
||||
|
||||
{% block extra_javascript %}
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
$("[rel=tooltip]").tooltip();
|
||||
});
|
||||
</script>
|
||||
{% endblock extra_javascript %}
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Fatlink view" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Fleet Activity Tracking" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "Participation data" %}</h1>
|
||||
<div>
|
||||
{% translate "Participation data" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
@@ -15,11 +24,15 @@
|
||||
</h4>
|
||||
</th>
|
||||
<th class="col-md-2 align-self-end">
|
||||
<a href="{% url 'fatlink:personal_statistics' %}" class="btn btn-info"><i class="fa-solid fa-circle-info fa-fw"></i>{% translate "Personal statistics" %}</a>
|
||||
<a href="{% url 'fatlink:personal_statistics' %}" class="btn btn-info">
|
||||
<i class="fa-solid fa-circle-info fa-fw"></i>
|
||||
{% translate "Personal statistics" %}
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if fats %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
@@ -30,6 +43,7 @@
|
||||
<th scope="col" class="text-center">{% translate "Ship" %}</th>
|
||||
<th scope="col" class="text-center">{% translate "Eve Time" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for fat in fats %}
|
||||
<tr>
|
||||
<td class="text-center">{{ fat.fatlink.fleet }}</td>
|
||||
@@ -48,6 +62,7 @@
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No fleet activity on record." %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.auth.fleetactivitytracking %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
@@ -66,6 +81,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if fatlinks %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
@@ -77,6 +93,7 @@
|
||||
<th scope="col" class="text-center">{% translate "Duration" %}</th>
|
||||
<th scope="col" class="text-center">{% translate "Edit" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for link in fatlinks %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
|
||||
@@ -352,11 +352,11 @@ def create_fatlink_view(request):
|
||||
for errorname, message in e.message_dict.items():
|
||||
messages.append(message[0].decode())
|
||||
context = {'form': form, 'errormessages': messages}
|
||||
return render(request, 'fleetactivitytracking/fatlinkformatter.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkcreate.html', context=context)
|
||||
else:
|
||||
form = FatlinkForm()
|
||||
context = {'form': form, 'badrequest': True}
|
||||
return render(request, 'fleetactivitytracking/fatlinkformatter.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkcreate.html', context=context)
|
||||
return redirect('fatlink:view')
|
||||
|
||||
else:
|
||||
@@ -365,7 +365,7 @@ def create_fatlink_view(request):
|
||||
|
||||
context = {'form': form}
|
||||
|
||||
return render(request, 'fleetactivitytracking/fatlinkformatter.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkcreate.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -377,12 +377,12 @@ def modify_fatlink_view(request, fat_hash=None):
|
||||
if request.GET.get('removechar', None):
|
||||
character_id = request.GET.get('removechar')
|
||||
character = EveCharacter.objects.get(character_id=character_id)
|
||||
logger.debug(f"Removing character {character.character_name} from fleetactivitytracking {fatlink}")
|
||||
logger.debug(f"Removing character {character.character_name} from fleetactivitytracking {fatlink}")
|
||||
|
||||
Fat.objects.filter(fatlink=fatlink).filter(character=character).delete()
|
||||
|
||||
if request.GET.get('deletefat', None):
|
||||
logger.debug("Removing fleetactivitytracking %s" % fatlink)
|
||||
logger.debug("Removing fleetactivitytracking %s" % fatlink)
|
||||
fatlink.delete()
|
||||
return redirect('fatlink:view')
|
||||
|
||||
|
||||
3
allianceauth/framework/__init__.py
Normal file
3
allianceauth/framework/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Alliance Auth Framework
|
||||
"""
|
||||
57
allianceauth/framework/api/evecharacter.py
Normal file
57
allianceauth/framework/api/evecharacter.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
Alliance Auth Evecharacter API
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from allianceauth.authentication.models import CharacterOwnership
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
from allianceauth.framework.api.user import get_sentinel_user
|
||||
|
||||
|
||||
def get_main_character_from_evecharacter(
|
||||
character: EveCharacter,
|
||||
) -> Optional[EveCharacter]:
|
||||
"""
|
||||
Get the main character for a given EveCharacter or None when no main character is set
|
||||
|
||||
:param character:
|
||||
:type character:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
try:
|
||||
userprofile = character.character_ownership.user.profile
|
||||
except (
|
||||
AttributeError,
|
||||
EveCharacter.character_ownership.RelatedObjectDoesNotExist,
|
||||
CharacterOwnership.user.RelatedObjectDoesNotExist,
|
||||
):
|
||||
return None
|
||||
|
||||
return userprofile.main_character
|
||||
|
||||
|
||||
def get_user_from_evecharacter(character: EveCharacter) -> User:
|
||||
"""
|
||||
Get the user for an EveCharacter or the sentinel user when no user is found
|
||||
|
||||
:param character:
|
||||
:type character:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
try:
|
||||
userprofile = character.character_ownership.user.profile
|
||||
except (
|
||||
AttributeError,
|
||||
EveCharacter.character_ownership.RelatedObjectDoesNotExist,
|
||||
CharacterOwnership.user.RelatedObjectDoesNotExist,
|
||||
):
|
||||
return get_sentinel_user()
|
||||
|
||||
return userprofile.user
|
||||
90
allianceauth/framework/api/user.py
Normal file
90
allianceauth/framework/api/user.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
Alliance Auth User API
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from allianceauth.authentication.models import CharacterOwnership
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
|
||||
|
||||
def get_all_characters_from_user(user: User) -> list:
|
||||
"""
|
||||
Get all characters from a user or an empty list
|
||||
when no characters are found for the user or the user is None
|
||||
|
||||
:param user:
|
||||
:type user:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
if user is None:
|
||||
return []
|
||||
|
||||
try:
|
||||
characters = [
|
||||
char.character for char in CharacterOwnership.objects.filter(user=user)
|
||||
]
|
||||
except AttributeError:
|
||||
return []
|
||||
|
||||
return characters
|
||||
|
||||
|
||||
def get_main_character_from_user(user: User) -> Optional[EveCharacter]:
|
||||
"""
|
||||
Get the main character from a user
|
||||
|
||||
:param user:
|
||||
:type user:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
if user is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
main_character = user.profile.main_character
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
return main_character
|
||||
|
||||
|
||||
def get_main_character_name_from_user(user: User) -> str:
|
||||
"""
|
||||
Get the main character name from a user
|
||||
|
||||
:param user:
|
||||
:type user:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
if user is None:
|
||||
sentinel_user = get_sentinel_user()
|
||||
|
||||
return sentinel_user.username
|
||||
|
||||
main_character = get_main_character_from_user(user=user)
|
||||
|
||||
try:
|
||||
username = main_character.character_name
|
||||
except AttributeError:
|
||||
return str(user)
|
||||
|
||||
return username
|
||||
|
||||
|
||||
def get_sentinel_user() -> User:
|
||||
"""
|
||||
Get the sentinel user or create one
|
||||
|
||||
:return:
|
||||
"""
|
||||
|
||||
return User.objects.get_or_create(username="deleted")[0]
|
||||
14
allianceauth/framework/apps.py
Normal file
14
allianceauth/framework/apps.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
Framework App Config
|
||||
"""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class FrameworkConfig(AppConfig):
|
||||
"""
|
||||
Framework App Config
|
||||
"""
|
||||
|
||||
name = "allianceauth.framework"
|
||||
label = "framework"
|
||||
@@ -13,6 +13,45 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Side Navigation
|
||||
------------------------------------------------------------------------------------- */
|
||||
@media all {
|
||||
#sidebar > div {
|
||||
width: 325px;
|
||||
}
|
||||
|
||||
/* Menu items in general */
|
||||
#sidebar-menu li > a,
|
||||
#sidebar-menu li > ul > li > a {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
max-width: 210px;
|
||||
}
|
||||
|
||||
/* Parent items with chevron and possible badge */
|
||||
#sidebar-menu li:has(span.badge) > a[data-bs-toggle="collapse"] {
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
/* Child items with possible badge */
|
||||
#sidebar-menu li > ul > li > a {
|
||||
max-width: 189px;
|
||||
}
|
||||
|
||||
/* Chevron icons */
|
||||
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-down,
|
||||
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-right {
|
||||
display: block;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-right,
|
||||
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-down {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cursor classes
|
||||
------------------------------------------------------------------------------------- */
|
||||
@media all {
|
||||
@@ -72,9 +111,16 @@
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border-left-width: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
margin-top: 1.25rem;
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.aa-callout.aa-callout-sm {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aa-callout.aa-callout-lg {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Last item bottom margin should be 0 */
|
||||
@@ -0,0 +1,8 @@
|
||||
{#Usage:#}
|
||||
{# {% include "framework/dashboard/widget-title.html" with title="Foobar" %}#}
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<h4 class="ms-auto me-auto mb-3">
|
||||
{{ title }}
|
||||
</h4>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
{#Usage:#}
|
||||
{# {% include "framework/header/page-header.html" with title="Foobar" subtitle="Barfoo" %}#}
|
||||
|
||||
{% if title %}
|
||||
<h1 class="page-header text-center mb-3">
|
||||
{{ title }}
|
||||
|
||||
{% if subtitle %}
|
||||
<br>
|
||||
<small class="text-muted">{{ subtitle }}</small>
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% endif %}
|
||||
3
allianceauth/framework/tests/__init__.py
Normal file
3
allianceauth/framework/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Initializing our tests
|
||||
"""
|
||||
179
allianceauth/framework/tests/test_api_user.py
Normal file
179
allianceauth/framework/tests/test_api_user.py
Normal file
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
Test sentinel user
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
# Django
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
|
||||
# Alliance Auth
|
||||
from allianceauth.framework.api.user import (
|
||||
get_sentinel_user,
|
||||
get_main_character_from_user,
|
||||
get_main_character_name_from_user
|
||||
)
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
|
||||
class TestSentinelUser(TestCase):
|
||||
"""
|
||||
Tests for the sentinel user
|
||||
"""
|
||||
|
||||
def test_should_create_user_when_it_does_not_exist(self) -> None:
|
||||
"""
|
||||
Test should create a sentinel user when it doesn't exist
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
# when
|
||||
user = get_sentinel_user()
|
||||
|
||||
# then
|
||||
self.assertEqual(first=user.username, second="deleted")
|
||||
|
||||
def test_should_return_user_when_it_does(self) -> None:
|
||||
"""
|
||||
Test should return sentinel user when it exists
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
# given
|
||||
User.objects.create_user(username="deleted")
|
||||
|
||||
# when
|
||||
user = get_sentinel_user()
|
||||
|
||||
# then
|
||||
self.assertEqual(first=user.username, second="deleted")
|
||||
|
||||
|
||||
class TestGetMainForUser(TestCase):
|
||||
"""
|
||||
Tests for get_main_character_from_user
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
"""
|
||||
Set up groups and users
|
||||
"""
|
||||
|
||||
super().setUpClass()
|
||||
|
||||
cls.character_name = "William T. Riker"
|
||||
cls.character_name_2 = "Christopher Pike"
|
||||
|
||||
cls.username = re.sub(pattern=r"[^\w\d@\.\+-]", repl="_", string=cls.character_name)
|
||||
cls.username_2 = re.sub(
|
||||
pattern=r"[^\w\d@\.\+-]", repl="_", string=cls.character_name_2
|
||||
)
|
||||
|
||||
cls.user = AuthUtils.create_user(username=cls.username)
|
||||
cls.user_without_main = AuthUtils.create_user(
|
||||
username=cls.username_2, disconnect_signals=True
|
||||
)
|
||||
|
||||
cls.character = AuthUtils.add_main_character_2(
|
||||
user=cls.user, name=cls.character_name, character_id=1001
|
||||
)
|
||||
|
||||
|
||||
def test_get_main_character_from_user_should_return_character_name(self):
|
||||
"""
|
||||
Test should return the main character name for a regular user
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
character = get_main_character_from_user(user=self.user)
|
||||
|
||||
self.assertEqual(first=character, second=self.character)
|
||||
|
||||
|
||||
def test_get_main_character_from_user_should_return_none_for_no_main_character(self):
|
||||
"""
|
||||
Test should return None for User without a main character
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
character = get_main_character_from_user(user=self.user_without_main)
|
||||
|
||||
self.assertIsNone(obj=character)
|
||||
|
||||
|
||||
def test_get_main_character_from_user_should_none(self):
|
||||
"""
|
||||
Test should return None when user is None
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
user = None
|
||||
|
||||
character = get_main_character_from_user(user=user)
|
||||
|
||||
self.assertIsNone(obj=character)
|
||||
|
||||
|
||||
def test_get_main_character_name_from_user_should_return_character_name(self):
|
||||
"""
|
||||
Test should return the main character name for a regular user
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
character_name = get_main_character_name_from_user(user=self.user)
|
||||
|
||||
self.assertEqual(first=character_name, second=self.character_name)
|
||||
|
||||
def test_get_main_character_name_from_user_should_return_user_name(self):
|
||||
"""
|
||||
Test should return just the username for a user without a main character
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
character_name = get_main_character_name_from_user(user=self.user_without_main)
|
||||
|
||||
self.assertEqual(first=character_name, second=self.username_2)
|
||||
|
||||
def test_get_main_character_name_from_user_should_return_sentinel_user(self):
|
||||
"""
|
||||
Test should return "deleted" as username (Sentinel User)
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
user = get_sentinel_user()
|
||||
|
||||
character_name = get_main_character_name_from_user(user=user)
|
||||
|
||||
self.assertEqual(first=character_name, second="deleted")
|
||||
|
||||
def test_get_main_character_name_from_user_should_return_sentinel_user_for_none(self):
|
||||
"""
|
||||
Test should return "deleted" (Sentinel User) if user is None
|
||||
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
user = None
|
||||
|
||||
character_name = get_main_character_name_from_user(user=user)
|
||||
|
||||
self.assertEqual(first=character_name, second="deleted")
|
||||
@@ -16,7 +16,7 @@ class GroupManagementMenuItem(MenuItemHook):
|
||||
MenuItemHook.__init__(
|
||||
self,
|
||||
text=_("Group Management"),
|
||||
classes="fas fa-users-cog fa-fw",
|
||||
classes="fa-solid fa-users-gear",
|
||||
url_name="groupmanagement:management",
|
||||
order=50,
|
||||
navactive=[
|
||||
@@ -36,7 +36,7 @@ class GroupManagementMenuItem(MenuItemHook):
|
||||
|
||||
"""
|
||||
<li class="d-flex m-2 p-2 pt-0 pb-0 mt-0 mb-0">
|
||||
<i class="fas fa-users fa-fw align-self-center me-2"></i>
|
||||
<i class="fa-solid fa-users fa-fw align-self-center me-2"></i>
|
||||
<a class="nav-link flex-fill align-self-center {% navactive request 'groupmanagement:groups' %}" href="{% url 'groupmanagement:groups' %}">
|
||||
{% translate "Groups" %}
|
||||
</a>
|
||||
@@ -49,7 +49,7 @@ class GroupsMenuItem(MenuItemHook):
|
||||
MenuItemHook.__init__(
|
||||
self,
|
||||
text=_("Groups"),
|
||||
classes="fas fa-user fa-fw",
|
||||
classes="fa-solid fa-user",
|
||||
url_name="groupmanagement:groups",
|
||||
order=25,
|
||||
navactive=[
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import functools
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models.functions import Lower
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
@@ -8,6 +12,39 @@ from .models import ReservedGroupName
|
||||
|
||||
|
||||
class GroupAdminForm(forms.ModelForm):
|
||||
users = forms.ModelMultipleChoiceField(
|
||||
queryset=User.objects.order_by(Lower('username')),
|
||||
required=False,
|
||||
widget=FilteredSelectMultiple(verbose_name=_("Users"), is_stacked=False),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if self.instance and self.instance.pk:
|
||||
self.fields["users"].initial = self.instance.user_set.all()
|
||||
|
||||
def save(self, commit=True):
|
||||
group: Group = super().save(commit=False)
|
||||
|
||||
if commit:
|
||||
group.save()
|
||||
|
||||
users = self.cleaned_data["users"]
|
||||
if group.pk:
|
||||
self._save_m2m_and_users(group, users)
|
||||
else:
|
||||
self.save_m2m = functools.partial(
|
||||
self._save_m2m_and_users, group=group, users=users
|
||||
)
|
||||
|
||||
return group
|
||||
|
||||
def _save_m2m_and_users(self, group, users):
|
||||
"""Save m2m relations incl. users."""
|
||||
group.user_set.set(users)
|
||||
self._save_m2m()
|
||||
|
||||
def clean_name(self):
|
||||
my_name = self.cleaned_data['name']
|
||||
if ReservedGroupName.objects.filter(name__iexact=my_name).exists():
|
||||
|
||||
@@ -74,11 +74,11 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='AuthGroup',
|
||||
fields=[
|
||||
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='auth.Group')),
|
||||
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='auth.Group')),
|
||||
('internal', models.BooleanField(default=True, help_text='Internal group, users cannot see, join or request to join this group.<br>Used for groups such as Members, Corp_*, Alliance_* etc.<br><b>Overrides Hidden and Open options when selected.</b>')),
|
||||
('hidden', models.BooleanField(default=True, help_text='Group is hidden from users but can still join with the correct link.')),
|
||||
('open', models.BooleanField(default=False, help_text='Group is open and users will be automatically added upon request. <br>If the group is not open users will need their request manually approved.')),
|
||||
('description', models.CharField(max_length=512, blank=True, help_text='Description of the group shown to users.', )),
|
||||
('description', models.CharField(max_length=512, blank=True, help_text='Description of the group shown to users.', )),
|
||||
|
||||
('group_leaders', models.ManyToManyField(related_name='leads_groups', to=settings.AUTH_USER_MODEL, blank=True, help_text='Group leaders can process group requests for this group specifically. Use the auth.group_management permission to allow a user to manage all groups.',)),
|
||||
],
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import Set
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -14,7 +13,7 @@ from allianceauth.notifications import notify
|
||||
class GroupRequest(models.Model):
|
||||
"""Request from a user for joining or leaving a group."""
|
||||
|
||||
leave_request = models.BooleanField(default=0)
|
||||
leave_request = models.BooleanField(default=False)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
|
||||
@@ -49,7 +48,7 @@ class RequestLog(models.Model):
|
||||
request_type = models.BooleanField(null=True)
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
request_info = models.CharField(max_length=254)
|
||||
action = models.BooleanField(default=0)
|
||||
action = models.BooleanField(default=False)
|
||||
request_actor = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
date = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load navactive %}
|
||||
|
||||
{% block page_title %}{{ group }} {% translate "Audit Log" %}{% endblock page_title %}
|
||||
{% block header_nav_brand %}{% translate "Audit Log" %} - {{ group.name }}{% endblock header_nav_brand %}
|
||||
{% block page_title %}
|
||||
{{ group }} {% translate "Audit Log" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Audit Log" %} - {{ group.name }}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block header_nav_collapse_left %}
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link {% navactive request 'groupmanagement:management' %}" href="{% url 'groupmanagement:management' %}">{% translate "Back" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% navactive request 'groupmanagement:management' %}" href="{% url 'groupmanagement:management' %}">{% translate "Back" %}</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@@ -66,60 +72,61 @@
|
||||
{% include 'bundles/datatables-js-bs5.html' %}
|
||||
{% include 'bundles/moment-js.html' with locale=True %}
|
||||
{% include 'bundles/filterdropdown-js.html' %}
|
||||
|
||||
<script>
|
||||
$.fn.dataTable.moment = (format, locale) => {
|
||||
const types = $.fn.dataTable.ext.type;
|
||||
|
||||
// Add type detection
|
||||
types.detect.unshift((d) => {
|
||||
return moment(d, format, locale, true).isValid() ?
|
||||
'moment-'+format :
|
||||
null;
|
||||
});
|
||||
|
||||
// Add sorting method - use an integer for the sorting
|
||||
types.order[ 'moment-'+format+'-pre' ] = (d) => {
|
||||
return moment(d, format, locale, true).unix();
|
||||
};
|
||||
};
|
||||
|
||||
$(document).ready(() => {
|
||||
$.fn.dataTable.moment('YYYY-MMM-D, HH:mm');
|
||||
|
||||
$('#log-entries').DataTable({
|
||||
order: [[0, 'desc'], [1, 'asc']],
|
||||
filterDropDown:
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
idx: 1
|
||||
},
|
||||
{
|
||||
idx: 2
|
||||
},
|
||||
{
|
||||
idx: 3
|
||||
},
|
||||
{
|
||||
idx: 4
|
||||
},
|
||||
{
|
||||
idx: 5
|
||||
},
|
||||
{
|
||||
idx: 6
|
||||
}
|
||||
],
|
||||
bootstrap: true,
|
||||
bootstrap_version: 5
|
||||
},
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'bundles/datatables-css-bs5.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
$.fn.dataTable.moment = function(format, locale) {
|
||||
let types = $.fn.dataTable.ext.type;
|
||||
|
||||
// Add type detection
|
||||
types.detect.unshift(function(d) {
|
||||
return moment(d, format, locale, true).isValid() ?
|
||||
'moment-'+format :
|
||||
null;
|
||||
});
|
||||
|
||||
// Add sorting method - use an integer for the sorting
|
||||
types.order[ 'moment-'+format+'-pre' ] = function(d) {
|
||||
return moment(d, format, locale, true).unix();
|
||||
};
|
||||
};
|
||||
|
||||
$(document).ready(function(){
|
||||
$.fn.dataTable.moment('YYYY-MMM-D, HH:mm');
|
||||
|
||||
$('#log-entries').DataTable({
|
||||
order: [[0, 'desc'], [1, 'asc']],
|
||||
filterDropDown:
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
idx: 1
|
||||
},
|
||||
{
|
||||
idx: 2
|
||||
},
|
||||
{
|
||||
idx: 3
|
||||
},
|
||||
{
|
||||
idx: 4
|
||||
},
|
||||
{
|
||||
idx: 5
|
||||
},
|
||||
{
|
||||
idx: 6
|
||||
}
|
||||
],
|
||||
bootstrap: true
|
||||
},
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
});
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load evelinks %}
|
||||
{% load navactive %}
|
||||
|
||||
{% block page_title %}{% translate "Group Members" %}{% endblock page_title %}
|
||||
{% block header_nav_brand %}{% translate "Group Members" %} - {{ group.name }}{% endblock header_nav_brand %}
|
||||
{% block page_title %}
|
||||
{% translate "Group Members" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Group Members" %} - {{ group.name }}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block header_nav_collapse_left %}
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link {% navactive request 'groupmanagement:management' %}" href="{% url 'groupmanagement:management' %}">{% translate "Back" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% navactive request 'groupmanagement:management' %}" href="{% url 'groupmanagement:management' %}">{% translate "Back" %}</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if group.user_set %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa" id="tab_group_members">
|
||||
<table class="table" id="tab_group_members">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Character" %}</th>
|
||||
@@ -30,6 +36,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ member.main_char|character_portrait_url:32 }}" class="rounded-circle" style="margin-right: 1rem;" alt="{{ member.main_char.character_name }}">
|
||||
|
||||
{% if member.main_char %}
|
||||
<a href="{{ member.main_char|evewho_character_url }}" target="_blank">
|
||||
{{ member.main_char.character_name }}
|
||||
@@ -39,7 +46,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if member.is_leader %}
|
||||
<i class="fa-solid fa-star"> title="{% translate "Group leader" %}" style="margin-left: 1rem;"></i>
|
||||
<sup><i class="fa-solid fa-star" title="{% translate "Group leader" %}"></i></sup>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -53,6 +60,7 @@
|
||||
{% translate "(unknown)" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-end">
|
||||
<a href="{% url 'groupmanagement:membership_remove' group.id member.user.id %}" class="btn btn-danger" title="{% translate "Remove from group" %}">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
@@ -77,24 +85,24 @@
|
||||
|
||||
{% block extra_javascript %}
|
||||
{% include 'bundles/datatables-js-bs5.html' %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
$('#tab_group_members').DataTable({
|
||||
order: [[0, "asc"]],
|
||||
columnDefs: [
|
||||
{
|
||||
"sortable": false,
|
||||
"targets": [2]
|
||||
},
|
||||
],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'bundles/datatables-css-bs5.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
$(document).ready(function(){
|
||||
$('#tab_group_members').DataTable({
|
||||
order: [[0, "asc"]],
|
||||
columnDefs: [
|
||||
{
|
||||
"sortable": false,
|
||||
"targets": [2]
|
||||
},
|
||||
],
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
});
|
||||
});
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
|
||||
{% block header_nav_collapse_left %}
|
||||
<li class="nav-item ">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% navactive request 'groupmanagement:management' %}" href="{% url 'groupmanagement:management' %}">{% translate "Join/Leave Requests" %}</a>
|
||||
</li>
|
||||
{% endblock header_nav_collapse_left %}
|
||||
@@ -17,7 +17,7 @@
|
||||
{% block content %}
|
||||
{% if groups %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Name" %}</th>
|
||||
@@ -54,15 +54,15 @@
|
||||
|
||||
<td class="text-end">
|
||||
<a href="{% url 'groupmanagement:membership' group.id %}" class="btn btn-primary" title="{% translate "View Members" %}">
|
||||
<i class="far fa-eye"></i>
|
||||
<i class="fa-regular fa-eye"></i>
|
||||
</a>
|
||||
|
||||
<a href="{% url "groupmanagement:audit_log" group.id %}" class="btn btn-info" title="{% translate "Audit Members" %}">
|
||||
<i class="far fa-list-alt"></i>
|
||||
<i class="fa-regular fa-rectangle-list"></i>
|
||||
</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" %}">
|
||||
<i class="far fa-clipboard"></i>
|
||||
<i class="fa-regular fa-clipboard"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "Available Groups" %}{% endblock page_title %}
|
||||
{% block header_nav_brand %}{% translate "Available Groups" %}{% endblock header_nav_brand %}
|
||||
{% block page_title %}
|
||||
{% translate "Available Groups" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Available Groups" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% if manager_perms %}
|
||||
{% block header_nav_collapse_left %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'groupmanagement:management' %}">{% translate "Group Management" %}
|
||||
{% if req_count %}
|
||||
<span class="badge bg-secondary">{{ req_count }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
{% block header_nav_collapse_left %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'groupmanagement:management' %}">{% translate "Group Management" %}
|
||||
{% if req_count %}
|
||||
<span class="badge bg-secondary">{{ req_count }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
{% if groups %}
|
||||
<table class="table" id="groupsTable" >
|
||||
@@ -23,7 +30,11 @@
|
||||
<tr>
|
||||
<th>{% translate "Name" %}</th>
|
||||
<th>{% translate "Description" %}</th>
|
||||
<th>{% translate "Leaders" %}<span class="m-1 fw-lighter badge bg-primary">{% translate "User" %}</span><span class="m-1 fw-lighter badge bg-secondary ">{% translate "Group" %}</span></th>
|
||||
<th>
|
||||
{% translate "Leaders" %}<br>
|
||||
<span class="my-1 me-1 fw-lighter badge bg-primary">{% translate "User" %}</span>
|
||||
<span class="my-1 me-1 fw-lighter badge bg-secondary">{% translate "Group" %}</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -32,13 +43,23 @@
|
||||
{% for g in groups %}
|
||||
<tr>
|
||||
<td>{{ g.group.name }}</td>
|
||||
<td>{{ g.group.authgroup.description|linebreaks|urlize }}</td>
|
||||
<td>
|
||||
{% if g.group.authgroup.description %}
|
||||
{{ g.group.authgroup.description|linebreaks|urlize }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="max-width: 30%;">
|
||||
{% if g.group.authgroup.group_leaders.all.count %}
|
||||
{% for leader in g.group.authgroup.group_leaders.all %}{% if leader.profile.main_character %}<span class="m-1 badge bg-primary">{{leader.profile.main_character}}</span>{% endif %}{% endfor %}
|
||||
{% for leader in g.group.authgroup.group_leaders.all %}
|
||||
{% if leader.profile.main_character %}
|
||||
<span class="my-1 me-1 badge bg-primary">{{leader.profile.main_character}}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if g.group.authgroup.group_leaders.all.count %}
|
||||
{% for group in g.group.authgroup.group_leader_groups.all %}<span class="badge bg-secondary">{{group.name}}</span>{% endfor %}
|
||||
{% for group in g.group.authgroup.group_leader_groups.all %}
|
||||
<span class="my-1 me-1 badge bg-secondary">{{group.name}}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@@ -78,16 +99,17 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
|
||||
{% block extra_javascript %}
|
||||
{% include 'bundles/datatables-js-bs5.html' %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
$('#groupsTable').DataTable();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'bundles/datatables-css-bs5.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
$(document).ready(function () {
|
||||
$('#groupsTable').DataTable();
|
||||
});
|
||||
{% endblock extra_script %}
|
||||
|
||||
@@ -1,107 +1,55 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load evelinks %}
|
||||
{% load navactive %}
|
||||
|
||||
{% block page_title %}{% translate "Groups Management" %}{% endblock page_title %}
|
||||
{% block header_nav_brand %}{% translate "Groups Management" %}{% endblock header_nav_brand %}
|
||||
{% block page_title %}
|
||||
{% translate "Groups Management" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% endblock extra_css %}
|
||||
{% block header_nav_brand %}
|
||||
{% translate "Groups Management" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block header_nav_collapse_left %}
|
||||
<li class="active">
|
||||
<a class="nav-link active" id="add-tab" data-bs-toggle="tab" data-bs-target="#add" type="button" role="tab" aria-controls="addd" aria-selected="true">
|
||||
{% translate "Join Requests" %}
|
||||
<li class="active">
|
||||
<a class="nav-link active" id="add-tab" data-bs-toggle="tab" data-bs-target="#add" type="button" role="tab" aria-controls="add" aria-selected="true">
|
||||
{% translate "Join Requests" %}
|
||||
|
||||
{% if acceptrequests %}
|
||||
<span class="badge bg-secondary">{{ acceptrequests|length }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if not auto_leave %}
|
||||
<li>
|
||||
<a class="nav-link" id="leave-tab" data-bs-toggle="tab" data-bs-target="#leave" type="button" role="tab" aria-controls="leave" aria-selected="false">
|
||||
{% translate "Leave Requests" %}
|
||||
|
||||
{% if leaverequests %}
|
||||
<span class="badge bg-secondary">{{ leaverequests|length }}</span>
|
||||
{% if acceptrequests %}
|
||||
<span class="badge bg-secondary">{{ acceptrequests|length }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link {% navactive request 'groupmanagement:membership groupmanagement:audit_log' %}" href="{% url 'groupmanagement:membership' %}">{% translate "Group Membership" %}</a>
|
||||
</li>
|
||||
|
||||
{% endblock %}
|
||||
{% if not show_leave_tab %}
|
||||
<li>
|
||||
<a class="nav-link" id="leave-tab" data-bs-toggle="tab" data-bs-target="#leave" type="button" role="tab" aria-controls="leave" aria-selected="false">
|
||||
{% translate "Leave Requests" %}
|
||||
|
||||
{% if leaverequests %}
|
||||
<span class="badge bg-secondary">{{ leaverequests|length }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link {% navactive request 'groupmanagement:membership groupmanagement:audit_log' %}" href="{% url 'groupmanagement:membership' %}">
|
||||
{% translate "Group Membership" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock header_nav_collapse_left %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="tab-content">
|
||||
<div id="add" class="tab-pane active">
|
||||
{% if acceptrequests %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th>{% translate "Organization" %}</th>
|
||||
<th>{% translate "Group" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="align-middle">
|
||||
{% for acceptrequest in acceptrequests %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ acceptrequest.main_char|character_portrait_url:32 }}" class="rounded-circle" style="margin-right: 1rem;" alt="{{ acceptrequest.main_char.character_name }}">
|
||||
{% if acceptrequest.main_char %}
|
||||
<a href="{{ acceptrequest.main_char|evewho_character_url }}" target="_blank">
|
||||
{{ acceptrequest.main_char.character_name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ acceptrequest.user.username }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if acceptrequest.main_char %}
|
||||
<a href="{{ acceptrequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||
{{ acceptrequest.main_char.corporation_name }}
|
||||
</a><br>
|
||||
{{ acceptrequest.main_char.alliance_name|default_if_none:"" }}
|
||||
{% else %}
|
||||
{% translate "(unknown)" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ acceptrequest.group.name }}</td>
|
||||
<td class="text-end">
|
||||
<a href="{% url 'groupmanagement:accept_request' acceptrequest.id %}" class="btn btn-success">
|
||||
{% translate "Accept" %}
|
||||
</a>
|
||||
|
||||
<a href="{% url 'groupmanagement:reject_request' acceptrequest.id %}" class="btn btn-danger">
|
||||
{% translate "Reject" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No group add requests." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if not auto_leave %}
|
||||
<div id="leave" class="tab-pane">
|
||||
{% if leaverequests %}
|
||||
<div class="tab-content">
|
||||
<div id="add" class="tab-pane active">
|
||||
{% if acceptrequests %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Character" %}</th>
|
||||
@@ -112,35 +60,39 @@
|
||||
</thead>
|
||||
|
||||
<tbody class="align-middle">
|
||||
{% for leaverequest in leaverequests %}
|
||||
{% for acceptrequest in acceptrequests %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="rounded-circle" style="margin-right: 1rem;" alt="{{ leaverequest.main_char.character_name }}">
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.character_name }}
|
||||
<img src="{{ acceptrequest.main_char|character_portrait_url:32 }}" class="rounded-circle" style="margin-right: 1rem;" alt="{{ acceptrequest.main_char.character_name }}">
|
||||
|
||||
{% if acceptrequest.main_char %}
|
||||
<a href="{{ acceptrequest.main_char|evewho_character_url }}" target="_blank">
|
||||
{{ acceptrequest.main_char.character_name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ leaverequest.user.username }}
|
||||
{{ acceptrequest.user.username }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.corporation_name }}
|
||||
</a><br>
|
||||
{{ leaverequest.main_char.alliance_name|default_if_none:"" }}
|
||||
{% if acceptrequest.main_char %}
|
||||
<a href="{{ acceptrequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||
{{ acceptrequest.main_char.corporation_name }}
|
||||
</a>
|
||||
<br>
|
||||
{{ acceptrequest.main_char.alliance_name|default_if_none:"" }}
|
||||
{% else %}
|
||||
{% translate "(unknown)" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ leaverequest.group.name }}</td>
|
||||
|
||||
<td>{{ acceptrequest.group.name }}</td>
|
||||
|
||||
<td class="text-end">
|
||||
<a href="{% url 'groupmanagement:leave_accept_request' leaverequest.id %}" class="btn btn-success">
|
||||
<a href="{% url 'groupmanagement:accept_request' acceptrequest.id %}" class="btn btn-success">
|
||||
{% translate "Accept" %}
|
||||
</a>
|
||||
|
||||
<a href="{% url 'groupmanagement:leave_reject_request' leaverequest.id %}" class="btn btn-danger">
|
||||
<a href="{% url 'groupmanagement:reject_request' acceptrequest.id %}" class="btn btn-danger">
|
||||
{% translate "Reject" %}
|
||||
</a>
|
||||
</td>
|
||||
@@ -150,9 +102,73 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No group leave requests." %}</div>
|
||||
<div class="aa-callout aa-callout-warning text-center">
|
||||
{% translate "No group add requests." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if not show_leave_tab %}
|
||||
<div id="leave" class="tab-pane">
|
||||
{% if leaverequests %}
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th>{% translate "Organization" %}</th>
|
||||
<th>{% translate "Group" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="align-middle">
|
||||
{% for leaverequest in leaverequests %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="rounded-circle" style="margin-right: 1rem;" alt="{{ leaverequest.main_char.character_name }}">
|
||||
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.character_name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ leaverequest.user.username }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.corporation_name }}
|
||||
</a>
|
||||
<br>
|
||||
{{ leaverequest.main_char.alliance_name|default_if_none:"" }}
|
||||
{% else %}
|
||||
{% translate "(unknown)" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>{{ leaverequest.group.name }}</td>
|
||||
|
||||
<td class="text-end">
|
||||
<a href="{% url 'groupmanagement:leave_accept_request' leaverequest.id %}" class="btn btn-success">
|
||||
{% translate "Accept" %}
|
||||
</a>
|
||||
|
||||
<a href="{% url 'groupmanagement:leave_reject_request' leaverequest.id %}" class="btn btn-danger">
|
||||
{% translate "Reject" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="aa-callout aa-callout-warning text-center">{% translate "No group leave requests." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -6,22 +6,22 @@ from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase, RequestFactory, Client, override_settings
|
||||
from django.test import Client, RequestFactory, TestCase, override_settings
|
||||
|
||||
from allianceauth.authentication.models import CharacterOwnership, State
|
||||
from allianceauth.eveonline.models import (
|
||||
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||
EveAllianceInfo, EveCharacter, EveCorporationInfo,
|
||||
)
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from . import get_admin_change_view_url
|
||||
from ..admin import HasLeaderFilter, GroupAdmin, Group
|
||||
from ..admin import Group, GroupAdmin, HasLeaderFilter
|
||||
from ..models import ReservedGroupName
|
||||
|
||||
from . import get_admin_change_view_url
|
||||
|
||||
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||
_has_auto_groups = True
|
||||
from allianceauth.eveonline.autogroups.models import AutogroupsConfig
|
||||
|
||||
from ..admin import IsAutoGroupFilter
|
||||
else:
|
||||
_has_auto_groups = False
|
||||
@@ -621,21 +621,16 @@ class TestGroupAdmin2(TestCase):
|
||||
response = self.client.post(
|
||||
f"/admin/groupmanagement/group/{group.pk}/change/",
|
||||
data={
|
||||
"name": f"{group.name}",
|
||||
"authgroup-TOTAL_FORMS": "1",
|
||||
"authgroup-INITIAL_FORMS": "1",
|
||||
"authgroup-MIN_NUM_FORMS": "0",
|
||||
"authgroup-MAX_NUM_FORMS": "1",
|
||||
"authgroup-0-description": "",
|
||||
"authgroup-0-states": f"{member_state.pk}",
|
||||
"name": group.name,
|
||||
"users": [user_member.pk, user_guest.pk],
|
||||
"authgroup-TOTAL_FORMS": 1,
|
||||
"authgroup-INITIAL_FORMS": 1,
|
||||
"authgroup-MIN_NUM_FORMS": 0,
|
||||
"authgroup-MAX_NUM_FORMS": 1,
|
||||
"authgroup-0-states": member_state.pk,
|
||||
"authgroup-0-internal": "on",
|
||||
"authgroup-0-hidden": "on",
|
||||
"authgroup-0-group": f"{group.pk}",
|
||||
"authgroup-__prefix__-description": "",
|
||||
"authgroup-__prefix__-internal": "on",
|
||||
"authgroup-__prefix__-hidden": "on",
|
||||
"authgroup-__prefix__-group": f"{group.pk}",
|
||||
"_save": "Save"
|
||||
"authgroup-0-group": group.pk,
|
||||
}
|
||||
)
|
||||
# then
|
||||
@@ -644,6 +639,85 @@ class TestGroupAdmin2(TestCase):
|
||||
self.assertIn(group, user_member.groups.all())
|
||||
self.assertNotIn(group, user_guest.groups.all())
|
||||
|
||||
def test_should_add_user_to_existing_group(self):
|
||||
# given
|
||||
user_bruce = AuthUtils.create_user("Bruce Wayne")
|
||||
user_lex = AuthUtils.create_user("Lex Luthor")
|
||||
group = Group.objects.create(name="dummy")
|
||||
user_bruce.groups.add(group)
|
||||
self.client.force_login(self.superuser)
|
||||
# when
|
||||
response = self.client.post(
|
||||
f"/admin/groupmanagement/group/{group.pk}/change/",
|
||||
data={
|
||||
"name": group.name,
|
||||
"users": [user_bruce.pk, user_lex.pk],
|
||||
"authgroup-TOTAL_FORMS": 1,
|
||||
"authgroup-INITIAL_FORMS": 1,
|
||||
"authgroup-MIN_NUM_FORMS": 0,
|
||||
"authgroup-MAX_NUM_FORMS": 1,
|
||||
"authgroup-0-internal": "on",
|
||||
"authgroup-0-hidden": "on",
|
||||
"authgroup-0-group": group.pk,
|
||||
}
|
||||
)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/groupmanagement/group/")
|
||||
self.assertIn(group, user_bruce.groups.all())
|
||||
self.assertIn(group, user_lex.groups.all())
|
||||
|
||||
def test_should_remove_user_from_existing_group(self):
|
||||
# given
|
||||
user_bruce = AuthUtils.create_user("Bruce Wayne")
|
||||
user_lex = AuthUtils.create_user("Lex Luthor")
|
||||
group = Group.objects.create(name="dummy")
|
||||
user_bruce.groups.add(group)
|
||||
user_lex.groups.add(group)
|
||||
self.client.force_login(self.superuser)
|
||||
# when
|
||||
response = self.client.post(
|
||||
f"/admin/groupmanagement/group/{group.pk}/change/",
|
||||
data={
|
||||
"name": group.name,
|
||||
"users": user_bruce.pk,
|
||||
"authgroup-TOTAL_FORMS": 1,
|
||||
"authgroup-INITIAL_FORMS": 1,
|
||||
"authgroup-MIN_NUM_FORMS": 0,
|
||||
"authgroup-MAX_NUM_FORMS": 1,
|
||||
"authgroup-0-internal": "on",
|
||||
"authgroup-0-hidden": "on",
|
||||
"authgroup-0-group": group.pk,
|
||||
}
|
||||
)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/groupmanagement/group/")
|
||||
self.assertIn(group, user_bruce.groups.all())
|
||||
self.assertNotIn(group, user_lex.groups.all())
|
||||
|
||||
def test_should_include_user_when_creating_group(self):
|
||||
# given
|
||||
user_bruce = AuthUtils.create_user("Bruce Wayne")
|
||||
self.client.force_login(self.superuser)
|
||||
# when
|
||||
response = self.client.post(
|
||||
"/admin/groupmanagement/group/add/",
|
||||
data={
|
||||
"name": "new group",
|
||||
"users": user_bruce.pk,
|
||||
"authgroup-TOTAL_FORMS": 1,
|
||||
"authgroup-INITIAL_FORMS": 0,
|
||||
"authgroup-MIN_NUM_FORMS": 0,
|
||||
"authgroup-MAX_NUM_FORMS": 1,
|
||||
}
|
||||
)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/groupmanagement/group/")
|
||||
group = Group.objects.get(name="new group")
|
||||
self.assertIn(group, user_bruce.groups.all())
|
||||
|
||||
|
||||
class TestReservedGroupNameAdmin(TestCase):
|
||||
@classmethod
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.test import RequestFactory, TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from allianceauth.groupmanagement.models import Group, GroupRequest
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from .. import views
|
||||
@@ -16,6 +17,7 @@ class TestViews(TestCase):
|
||||
self.factory = RequestFactory()
|
||||
self.user = AuthUtils.create_user('Peter Parker')
|
||||
self.user_with_manage_permission = AuthUtils.create_user('Bruce Wayne')
|
||||
self.group = Group.objects.create(name="Example group")
|
||||
|
||||
# set permissions
|
||||
AuthUtils.add_permission_to_user_by_name(
|
||||
@@ -83,3 +85,19 @@ class TestViews(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotIn('id="leave-tab" data-bs-toggle="tab" data-bs-target="#leave"', content)
|
||||
self.assertNotIn('<div id="leave" class="tab-pane">', content)
|
||||
|
||||
@override_settings(GROUPMANAGEMENT_AUTO_LEAVE=True)
|
||||
def test_should_not_hide_leave_requests_tab_when_there_are_open_requests(self):
|
||||
# given
|
||||
request = self.factory.get(reverse('groupmanagement:management'))
|
||||
request.user = self.user_with_manage_permission
|
||||
GroupRequest.objects.create(user=self.user, group=self.group, leave_request=True)
|
||||
|
||||
# when
|
||||
response = views.group_management(request)
|
||||
|
||||
# then
|
||||
content = response_content_to_str(response)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('<a class="nav-link" id="leave-tab" data-bs-toggle="tab" data-bs-target="#leave"', content)
|
||||
self.assertIn('<div id="leave" class="tab-pane">', content)
|
||||
|
||||
@@ -2,13 +2,12 @@ import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
||||
from django.db.models import Count
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from allianceauth.notifications import notify
|
||||
@@ -16,7 +15,6 @@ from allianceauth.notifications import notify
|
||||
from .managers import GroupManager
|
||||
from .models import GroupRequest, RequestLog
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -45,10 +43,15 @@ def group_management(request):
|
||||
logger.debug("Providing user {} with {} acceptrequests and {} leaverequests.".format(
|
||||
request.user, len(acceptrequests), len(leaverequests)))
|
||||
|
||||
show_leave_tab = (
|
||||
getattr(settings, 'GROUPMANAGEMENT_AUTO_LEAVE', False)
|
||||
and not GroupRequest.objects.filter(leave_request=True).exists()
|
||||
)
|
||||
|
||||
render_items = {
|
||||
'acceptrequests': acceptrequests,
|
||||
'leaverequests': leaverequests,
|
||||
'auto_leave': getattr(settings, 'GROUPMANAGEMENT_AUTO_LEAVE', False),
|
||||
'show_leave_tab': show_leave_tab,
|
||||
}
|
||||
|
||||
return render(request, 'groupmanagement/index.html', context=render_items)
|
||||
|
||||
@@ -13,7 +13,7 @@ class ChoiceInline(admin.TabularInline):
|
||||
@admin.register(ApplicationQuestion)
|
||||
class QuestionAdmin(admin.ModelAdmin):
|
||||
fieldsets = [
|
||||
(None, {'fields': ['title', 'help_text', 'multi_select']}),
|
||||
(None, {'fields': ['title', 'help_text', 'multi_select']}),
|
||||
]
|
||||
inlines = [ChoiceInline]
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class ApplicationsMenu(MenuItemHook):
|
||||
MenuItemHook.__init__(
|
||||
self,
|
||||
_('Applications'),
|
||||
'far fa-file fa-fw',
|
||||
'fa-regular fa-file',
|
||||
'hrapplications:index',
|
||||
navactive=['hrapplications:'])
|
||||
|
||||
|
||||
@@ -1,22 +1,37 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "Choose a Corp" %}{% endblock page_title %}
|
||||
{% block page_title %}
|
||||
{% translate "Choose a Corp" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "HR Application Management" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "Choose a Corp" %}</h1>
|
||||
<div>
|
||||
{% translate "Choose a Corp" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
{% if choices %}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">{% translate "Available Corps" %}</div>
|
||||
<table class="table table-responsive">
|
||||
{% for choice in choices %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:create_view' choice.0 %}" class="btn btn-primary" title="Apply">{{ choice.1 }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">{% translate "Available Corps" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table table-responsive">
|
||||
{% for choice in choices %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:create_view' choice.0 %}" class="btn btn-primary" title="Apply">{{ choice.1 }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-danger">{% translate "No corps are accepting applications at this time." %}</div>
|
||||
|
||||
@@ -1,35 +1,66 @@
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
{% load django_bootstrap5 %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "HR Application Management" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "Apply To" %} {{ corp.corporation_name }}</h1>
|
||||
<div class="container-fluid">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="row">
|
||||
<form class="form-signin">
|
||||
{% csrf_token %}
|
||||
{% for question in questions %}
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="id_{{ question.pk }}">{{ question.title }}</label>
|
||||
<div class=" ">
|
||||
{% if question.help_text %}
|
||||
<div class="text-center">{{ question.help_text }}</div>
|
||||
{% endif %}
|
||||
{% for choice in question.choices.all %}
|
||||
<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="id_{{ question.pk }}_choice_{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
|
||||
{% empty %}
|
||||
<textarea class="form-control" cols="30" id="id_{{ question.pk }}" name="{{ question.pk }}" rows="4"></textarea>
|
||||
<div>
|
||||
<h1 class="page-header text-center mb-3">
|
||||
{% translate "Apply To" %} {{ corp.corporation_name }}
|
||||
</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">
|
||||
{% translate "Application form" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{% for question in questions %}
|
||||
<div class="card mb-3 form-group border-0">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">{{ question.title }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
{% if question.help_text %}
|
||||
<p class="text-muted">
|
||||
{{ question.help_text }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% for choice in question.choices.all %}
|
||||
<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="id_{{ question.pk }}_choice_{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
|
||||
{% empty %}
|
||||
<textarea class="form-control" cols="30" id="id_{{ question.pk }}" name="{{ question.pk }}" rows="10"></textarea>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="form-group clearfix">
|
||||
{% translate "Submit" as button_text %}
|
||||
{% bootstrap_button button_class="btn btn-primary" content=button_text name="submitApplicationForm" id="submitApplicationForm" %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit" formmethod="post">{% translate "Submit" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,193 +1,215 @@
|
||||
{% extends "allianceauth/base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "HR Application Management" %}{% endblock page_title %}
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
{% block page_title %}
|
||||
{% translate "HR Application Management" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "HR Application Management" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "Personal Applications" %}
|
||||
<div class="text-end">
|
||||
{% if create %}
|
||||
<a href="{% url 'hrapplications:create_view' %}">
|
||||
<button type="button" class="btn btn-success">{% translate "Create Application" %}</button>
|
||||
</a>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-success" disabled>{% translate "Create Application" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</h1>
|
||||
{% if personal_apps %}
|
||||
<div class="panel panel-default">
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Username" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}
|
||||
<th class="text-center">{% translate "Status" %}</th>
|
||||
<th class="text-center">{% translate "Actions" %}</th>
|
||||
</tr>
|
||||
{% for personal_app in personal_apps %}
|
||||
<tr>
|
||||
<td class="text-center">{{ personal_app.user.username }}</td>
|
||||
<td class="text-center">{{ personal_app.form.corp.corporation_name }}</td>
|
||||
<td class="text-center">
|
||||
{% if personal_app.approved == None %}
|
||||
<div class="badge bg-warning">{% translate "Pending" %}</div>
|
||||
{% elif personal_app.approved == True %}
|
||||
<div class="badge bg-success">{% translate "Approved" %}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-danger">{% translate "Rejected" %}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:personal_view' personal_app.id %}" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
</a>
|
||||
<div>
|
||||
{% translate "Personal Applications" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
{% if personal_app.approved == None %}
|
||||
<a href="{% url 'hrapplications:personal_removal' personal_app.id %}" class="btn btn-danger">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<div class="text-end mb-3">
|
||||
{% if create %}
|
||||
<a href="{% url 'hrapplications:create_view' %}">
|
||||
<button type="button" class="btn btn-success">{% translate "Create Application" %}</button>
|
||||
</a>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-success" disabled>{% translate "Create Application" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if personal_apps %}
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-body">
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Username" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}
|
||||
<th class="text-center">{% translate "Status" %}</th>
|
||||
<th class="text-center">{% translate "Actions" %}</th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% for personal_app in personal_apps %}
|
||||
<tr>
|
||||
<td class="text-center">{{ personal_app.user.username }}</td>
|
||||
<td class="text-center">{{ personal_app.form.corp.corporation_name }}</td>
|
||||
<td class="text-center">
|
||||
{% if personal_app.approved == None %}
|
||||
<div class="badge bg-warning">{% translate "Pending" %}</div>
|
||||
{% elif personal_app.approved == True %}
|
||||
<div class="badge bg-success">{% translate "Approved" %}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-danger">{% translate "Rejected" %}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:personal_view' personal_app.id %}" class="btn btn-primary">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</a>
|
||||
|
||||
{% if personal_app.approved == None %}
|
||||
<a href="{% url 'hrapplications:personal_removal' personal_app.id %}" class="btn btn-danger">
|
||||
<i class="fa-solid fa-trash-can"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.auth.human_resources %}
|
||||
<h1 class="page-header text-center">{% translate "Application Management" %}
|
||||
<div class="text-end">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#myModal">
|
||||
{% translate "Search Applications" %}
|
||||
</button>
|
||||
</div>
|
||||
</h1>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" href="#pending">{% translate "Pending" %}</a></li>
|
||||
<li><a data-toggle="tab" href="#reviewed">{% translate "Reviewed" %}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div id="pending" class="tab-pane fade in active panel panel-default">
|
||||
<div class="panel-body">
|
||||
{% if applications %}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Date" %}</th>
|
||||
<th class="text-center">{% translate "Username" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Status" %}</th>
|
||||
<th class="text-center">{% translate "Actions" %}</th>
|
||||
</tr>
|
||||
{% for app in applications %}
|
||||
{% translate "Application Management" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
<div class="text-end mb-3">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#modal-hr-search">
|
||||
{% translate "Search Applications" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card card-default">
|
||||
<div class="card-body clearfix">
|
||||
<ul class="nav nav-tabs" id="application-list" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<a
|
||||
class="nav-link active"
|
||||
id="pending"
|
||||
data-bs-toggle="tab"
|
||||
href="#tab-pending"
|
||||
role="tab"
|
||||
aria-controls="tab-pending"
|
||||
aria-selected="true"
|
||||
>
|
||||
{% translate "Pending" %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" role="presentation">
|
||||
<a
|
||||
class="nav-link"
|
||||
id="reviewed"
|
||||
data-bs-toggle="tab"
|
||||
href="#tab-reviewed"
|
||||
role="tab"
|
||||
aria-controls="tab-reviewed"
|
||||
aria-selected="false"
|
||||
>
|
||||
{% translate "Reviewed" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="application-list-content">
|
||||
<div id="tab-pending" class="tab-pane fade show active" role="tabpanel" aria-labelledby="tab-pending">
|
||||
{% if applications %}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td class="text-center">{{ app.created }}</td>
|
||||
<td class="text-center">{{ app.user.username }}</td>
|
||||
<td class="text-center">{{ app.main_character }}</td>
|
||||
<td class="text-center">{{ app.form.corp.corporation_name }}</td>
|
||||
<td class="text-center">
|
||||
{% if app.approved == None %}
|
||||
{% if app.reviewer_str %}
|
||||
<div class="badge bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-warning">{% translate "Pending" %}</div>
|
||||
{% endif %}
|
||||
{% elif app.approved == True %}
|
||||
<div class="badge bg-success">{% translate "Approved" %}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-danger">{% translate "Rejected" %}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:view' app.id %}" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
</a>
|
||||
</td>
|
||||
<th class="text-center">{% translate "Date" %}</th>
|
||||
<th class="text-center">{% translate "Username" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Status" %}</th>
|
||||
<th class="text-center">{% translate "Actions" %}</th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No pending applications." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="reviewed" class="tab-pane fade panel panel-default">
|
||||
<div class="panel-body">
|
||||
{% if finished_applications %}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Date" %}</th>
|
||||
<th class="text-center">{% translate "Username" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Status" %}</th>
|
||||
<th class="text-center">{% translate "Actions" %}</th>
|
||||
</tr>
|
||||
{% for app in finished_applications %}
|
||||
<tr>
|
||||
<td class="text-center">{{ app.created }}</td>
|
||||
<td class="text-center">{{ app.user.username }}</td>
|
||||
<td class="text-center">{{ app.main_character }}</td>
|
||||
<td class="text-center">{{ app.form.corp.corporation_name }}</td>
|
||||
<td class="text-center">
|
||||
{% if app.approved == None %}
|
||||
{% if app.reviewer_str %}
|
||||
<div class="badge bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
|
||||
|
||||
{% for app in applications %}
|
||||
<tr>
|
||||
<td class="text-center">{{ app.created }}</td>
|
||||
<td class="text-center">{{ app.user.username }}</td>
|
||||
<td class="text-center">{{ app.main_character }}</td>
|
||||
<td class="text-center">{{ app.form.corp.corporation_name }}</td>
|
||||
<td class="text-center">
|
||||
{% if app.approved == None %}
|
||||
{% if app.reviewer_str %}
|
||||
<div class="badge bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-warning">{% translate "Pending" %}</div>
|
||||
{% endif %}
|
||||
{% elif app.approved == True %}
|
||||
<div class="badge bg-success">{% translate "Approved" %}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-warning">{% translate "Pending" %}</div>
|
||||
<div class="badge bg-danger">{% translate "Rejected" %}</div>
|
||||
{% endif %}
|
||||
{% elif app.approved == True %}
|
||||
<div class="badge bg-success">{% translate "Approved" %}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-danger">{% translate "Rejected" %}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:view' app.id %}" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
</a>
|
||||
{% if perms.hrapplications.delete_application %}
|
||||
<a href="{% url 'hrapplications:remove' app.id %}" class="btn btn-danger">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:view' app.id %}" class="btn btn-primary">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No pending applications." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="tab-reviewed" class="tab-pane fade" role="tabpanel" aria-labelledby="tab-reviewed">
|
||||
{% if finished_applications %}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Date" %}</th>
|
||||
<th class="text-center">{% translate "Username" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Status" %}</th>
|
||||
<th class="text-center">{% translate "Actions" %}</th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No reviewed applications." %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% for app in finished_applications %}
|
||||
<tr>
|
||||
<td class="text-center">{{ app.created }}</td>
|
||||
<td class="text-center">{{ app.user.username }}</td>
|
||||
<td class="text-center">{{ app.main_character }}</td>
|
||||
<td class="text-center">{{ app.form.corp.corporation_name }}</td>
|
||||
<td class="text-center">
|
||||
{% if app.approved == None %}
|
||||
{% if app.reviewer_str %}
|
||||
<div class="badge bg-info">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-warning">{% translate "Pending" %}</div>
|
||||
{% endif %}
|
||||
{% elif app.approved == True %}
|
||||
<div class="badge bg-success">{% translate "Approved" %}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-danger">{% translate "Rejected" %}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'hrapplications:view' app.id %}" class="btn btn-primary">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</a>
|
||||
|
||||
{% if perms.hrapplications.delete_application %}
|
||||
<a href="{% url 'hrapplications:remove' app.id %}" class="btn btn-danger">
|
||||
<i class="fa-solid fa-trash-can"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No reviewed applications." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if perms.auth.human_resources %}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">{% translate "Close" %}</span></button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% translate "Application Search" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-signin" role="form" action="{% url 'hrapplications:search' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{ search_form|bootstrap }}
|
||||
<br>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Search" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "hrapplications/partials/modals/search.html" %}
|
||||
{% endblock content %}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{% load django_bootstrap5 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% if perms.auth.human_resources %}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="modal-hr-search" tabindex="-1" aria-labelledby="modalHrSearch" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title fs-5" id="modalHrSearchLabel">
|
||||
{% translate "Application Search" %}
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Close' %}"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form class="form-signin" role="form" action="{% url 'hrapplications:search' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
|
||||
{% bootstrap_form search_form %}
|
||||
|
||||
<div class="form-group mt-3 clearfix">
|
||||
{% translate "Search" as button_text %}
|
||||
{% bootstrap_button button_class="btn btn-primary" content=button_text name="search" id="search" %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -1,49 +1,58 @@
|
||||
{% extends "allianceauth/base.html" %}
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "HR Application Management" %}{% endblock page_title %}
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
{% block page_title %}
|
||||
{% translate "HR Application Management" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "HR Application Management" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<div>
|
||||
{% if perms.auth.human_resources %}
|
||||
<h1 class="page-header text-center">{% translate "Application Search Results" %}
|
||||
<div class="text-end">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#myModal">
|
||||
{% translate "Search Applications" %}
|
||||
</button>
|
||||
</div>
|
||||
</h1>
|
||||
<div class="container-fluid">
|
||||
<table class="table table-bordered">
|
||||
{% translate "Application Search Results" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
<div class="text-end mb-3">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#modal-hr-search">
|
||||
{% translate "Search Applications" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "Application ID" %}</th>
|
||||
<th class="text-center">{% translate "Username" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
<th class="text-center">{% translate "Corporation" %}</th>
|
||||
<th>{% translate "Application ID" %}</th>
|
||||
<th>{% translate "Username" %}</th>
|
||||
<th>{% translate "Main Character" %}</th>
|
||||
<th>{% translate "Corporation" %}</th>
|
||||
<th class="text-center">{% translate "Status" %}</th>
|
||||
<th class="text-center">{% translate "Actions" %}</th>
|
||||
<th class="text-end">{% translate "Actions" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for app in applications %}
|
||||
<tr>
|
||||
<td class="text-center">{{ app.id }}</td>
|
||||
<td class="text-center">{{ app.user }}</td>
|
||||
<td class="text-center">{{ app.main_character }}</td>
|
||||
<td class="text-center">{{ app.form.corp }}</td>
|
||||
<td>{{ app.id }}</td>
|
||||
<td>{{ app.user }}</td>
|
||||
<td >{{ app.main_character }}</td>
|
||||
<td>{{ app.form.corp }}</td>
|
||||
<td class="text-center">
|
||||
{% if app.approved == None %}
|
||||
<div class="badge bg-warning">{% translate "Pending" %}</div>
|
||||
{% elif app.approved == True %}
|
||||
{% elif app.approved == True %}
|
||||
<div class="badge bg-success">{% translate "Approved" %}</div>
|
||||
{% else %}
|
||||
<div class="badge bg-danger">{% translate "Rejected" %}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<td class="text-end">
|
||||
<a href="{% url 'hrapplications:view' app.id %}" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -53,27 +62,5 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if perms.auth.human_resources %}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">{% translate "Close" %}</span></button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% translate "Application Search" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-signin" role="form" action="{% url 'hrapplications:search' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{ search_form|bootstrap }}
|
||||
<br>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Search" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "hrapplications/partials/modals/search.html" %}
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,149 +1,179 @@
|
||||
{% extends "allianceauth/base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% extends "allianceauth/base-bs5.html" %}
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "View Application" %}{% endblock page_title %}
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
{% block page_title %}
|
||||
{% translate "View Application" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block header_nav_brand %}
|
||||
{% translate "HR Application Management" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% translate "View Application" %}</h1>
|
||||
<div class="container-fluid">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="row">
|
||||
{% if app.approved %}
|
||||
<div class="alert alert-success text-center">{% translate "Approved" %}</div>
|
||||
{% elif app.approved == False %}
|
||||
<div class="alert alert-danger text-center">{% translate "Denied" %}</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "Pending" %}</div>
|
||||
{% endif %}
|
||||
{% if app.reviewer_str %}
|
||||
<div class="alert alert-info text-center">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">{% translate "Applicant" %}</div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "User" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center">{{ app.user }}</td>
|
||||
<td class="text-center">{{ app.main_character }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">{% translate "Characters" %}</div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center"></th>
|
||||
<th class="text-center">{% translate "Name" %}</th>
|
||||
<th class="text-center">{% translate "Corp" %}</th>
|
||||
<th class="text-center">{% translate "Alliance" %}</th>
|
||||
</tr>
|
||||
{% for char in app.characters %}
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<img class="ra-avatar img-responsive img-circle" src="{{ char.portrait_url_32 }}" alt="{{ 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.alliance_name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
{% for response in responses %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{{ response.question.title }}</div>
|
||||
<div class="alert">{{ response.answer|linebreaksbr }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if buttons %}
|
||||
{% if perms.auth.human_resources %}
|
||||
<div class="row">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">{% translate "Actions" %}</div>
|
||||
<div class="panel-body text-center">
|
||||
{% if app.approved == None %}
|
||||
{% if app.reviewer == user %}
|
||||
{% if perms.hrapplications.approve_application %}
|
||||
<a href="{% url 'hrapplications:approve' app.id %}" class="btn btn-success">{% translate "Approve" %}</a>
|
||||
{% endif %}
|
||||
{% if perms.hrapplications.reject_application %}
|
||||
<a href="{% url 'hrapplications:reject' app.id %}" class="btn btn-danger">{% translate "Reject" %}</a>
|
||||
{% endif %}
|
||||
{% if perms.hrapplications.delete_application %}
|
||||
<a href="{% url 'hrapplications:remove' app.id %}" class="btn btn-danger">{% translate "Delete" %}</a>
|
||||
{% endif %}
|
||||
{% elif not app.reviewer %}
|
||||
<a href="{% url 'hrapplications:mark_in_progress' app.id %}" class="btn btn-warning">{% translate "Mark in Progress" %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if perms.hrapplications.add_applicationcomment %}
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal">{% translate "Comment" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="headingThree">
|
||||
<h4 class="panel-title">
|
||||
<a class="collapsed" data-toggle="collapse" data-parent="#accordion" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
{% translate 'Comments' %} ({{ comments.count }})
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseThree" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
|
||||
<div class="panel-body">
|
||||
{% for comment in comments %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="">
|
||||
<div class="panel-title">
|
||||
<div class="pull-right">{{ comment.created }}</div>
|
||||
<div class="pull-left">{% if comment.user.profile.main_character %}{{ comment.user.profile.main_character }}{% else %}{{ comment.user }}{% endif %}</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">{{ comment.text|linebreaks }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div>
|
||||
{% translate "View Application" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
<div>
|
||||
{% if app.approved %}
|
||||
<div class="alert alert-success text-center">{% translate "Approved" %}</div>
|
||||
{% elif app.approved == False %}
|
||||
<div class="alert alert-danger text-center">{% translate "Denied" %}</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "Pending" %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if app.reviewer_str %}
|
||||
<div class="alert alert-info text-center">{% translate "Reviewer:" %} {{ app.reviewer_str }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-info">
|
||||
<div class="card-title mb-0">{% translate "Applicant" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% translate "User" %}</th>
|
||||
<th class="text-center">{% translate "Main Character" %}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center">{{ app.user }}</td>
|
||||
<td class="text-center">{{ app.main_character }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-info">
|
||||
<div class="card-title mb-0">{% translate "Characters" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% translate "Name" %}</th>
|
||||
<th>{% translate "Corporation" %}</th>
|
||||
<th>{% translate "Alliance" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for char in app.characters %}
|
||||
<tr>
|
||||
<td>
|
||||
<img class="ra-avatar img-responsive rounded-circle" src="{{ char.portrait_url_32 }}" alt="{{ char.character_name }}">
|
||||
</td>
|
||||
<td>{{ char.character_name }}</td>
|
||||
<td>{{ char.corporation_name }}</td>
|
||||
<td>{{ char.alliance_name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for response in responses %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">{{ response.question.title }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">{{ response.answer|linebreaksbr }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% if buttons %}
|
||||
{% if perms.auth.human_resources %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">{% translate "Actions" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body text-center">
|
||||
{% if app.approved == None %}
|
||||
{% if app.reviewer == user %}
|
||||
{% if perms.hrapplications.approve_application %}
|
||||
<a href="{% url 'hrapplications:approve' app.id %}" class="btn btn-success">{% translate "Approve" %}</a>
|
||||
{% endif %}
|
||||
{% if perms.hrapplications.reject_application %}
|
||||
<a href="{% url 'hrapplications:reject' app.id %}" class="btn btn-warning">{% translate "Reject" %}</a>
|
||||
{% endif %}
|
||||
{% if perms.hrapplications.delete_application %}
|
||||
<a href="{% url 'hrapplications:remove' app.id %}" class="btn btn-danger">{% translate "Delete" %}</a>
|
||||
{% endif %}
|
||||
{% elif not app.reviewer %}
|
||||
<a href="{% url 'hrapplications:mark_in_progress' app.id %}" class="btn btn-warning">{% translate "Mark in Progress" %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if perms.hrapplications.add_applicationcomment %}
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modal-hr-comment">{% translate "Comment" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||
<div class="card card-default">
|
||||
<div class="card-header">
|
||||
<div class="card-title mb-0">
|
||||
{% translate 'Comments' %} ({{ comments.count }})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
{% if comments %}
|
||||
{% for comment in comments %}
|
||||
<div class="card card-default">
|
||||
<div class="card-header" role="tab" id="">
|
||||
<div class="card-title mb-0 clearfix">
|
||||
<div class="float-md-end">{{ comment.created }}</div>
|
||||
<div class="float-md-start">{% if comment.user.profile.main_character %}{{ comment.user.profile.main_character }}{% else %}{{ comment.user }}{% endif %}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">{{ comment.text|linebreaks }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
{% translate "No comments" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if perms.hrapplications.add_applicationcomment %}
|
||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal fade" id="modal-hr-comment" tabindex="-1" aria-labelledby="modalHrComment" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span aria-hidden="true">×</span><span class="sr-only">{% translate "Close" %}</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% translate "Add Comment" %}</h4>
|
||||
<div class="modal-title fs-5" id="modalHrCommentLabel">
|
||||
{% translate "Add Comment" %}
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Close' %}"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form class="form-signin" role="form" action="" method="POST">
|
||||
{% csrf_token %}
|
||||
{{ comment_form|bootstrap }}
|
||||
<br>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Add Comment" %}</button>
|
||||
|
||||
{% bootstrap_form comment_form %}
|
||||
|
||||
<div class="form-group mt-3 clearfix">
|
||||
{% translate "Add comment" as button_text %}
|
||||
{% bootstrap_button button_class="btn btn-primary" content=button_text name="addComment" id="addComment" %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
BIN
allianceauth/locale/cs/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/cs/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2776
allianceauth/locale/cs/LC_MESSAGES/django.po
Normal file
2776
allianceauth/locale/cs/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
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/nl/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/nl/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2789
allianceauth/locale/nl/LC_MESSAGES/django.po
Normal file
2789
allianceauth/locale/nl/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
allianceauth/locale/pl_PL/LC_MESSAGES/django.mo
Normal file
BIN
allianceauth/locale/pl_PL/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2848
allianceauth/locale/pl_PL/LC_MESSAGES/django.po
Normal file
2848
allianceauth/locale/pl_PL/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user