mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 14:16:21 +01:00
Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da93940e13 | ||
|
|
f53b43d9dc | ||
|
|
497a167ca7 | ||
|
|
852c5a3037 | ||
|
|
90f6777a7a | ||
|
|
a8d890abaf | ||
|
|
79379b444c | ||
|
|
ace1de5c68 | ||
|
|
5d6128e9ea | ||
|
|
131cc5ed0a | ||
|
|
9297bed43f | ||
|
|
b2fddc683a | ||
|
|
9af634d16a | ||
|
|
a68163caa3 | ||
|
|
00770fd034 | ||
|
|
01164777ed | ||
|
|
00f5e3e1e0 | ||
|
|
8b2527f408 | ||
|
|
b7500e4e4e | ||
|
|
4f4bd0c419 | ||
|
|
8ae4e02012 | ||
|
|
cc9a07197d | ||
|
|
f18dd1029b | ||
|
|
fd8d43571a | ||
|
|
13e88492f1 | ||
|
|
38df580a56 | ||
|
|
ba39318313 | ||
|
|
d8c6035405 | ||
|
|
2ef3da916b | ||
|
|
d32d8b26ce | ||
|
|
f348b1a34c | ||
|
|
86aaa3edda | ||
|
|
26017056c7 | ||
|
|
e39a3c072b | ||
|
|
827291dda4 | ||
|
|
ea8958ccc3 | ||
|
|
20554df857 | ||
|
|
750f43eaf0 | ||
|
|
09cf28ec9f | ||
|
|
b61746b3cb | ||
|
|
22c22fafeb | ||
|
|
577c4395c4 | ||
|
|
d241f476f7 | ||
|
|
5832ed0c30 | ||
|
|
bd9ea225be | ||
|
|
4a575dd70c | ||
|
|
b80ee16a7c | ||
|
|
c888371e6c | ||
|
|
8de2c3bfcb | ||
|
|
6688f73565 | ||
|
|
7d929cb6e2 | ||
|
|
72740b9e4d | ||
|
|
f7d279fa16 | ||
|
|
ff7c9c48f3 | ||
|
|
d11832913d | ||
|
|
724e0e83f2 | ||
|
|
333f091f1a | ||
|
|
cfbb0b993a | ||
|
|
582b6754a4 | ||
|
|
7767c46bf4 | ||
|
|
bf34cef896 | ||
|
|
c085ec6860 | ||
|
|
5f5d0316b2 | ||
|
|
f9ec64c3ad | ||
|
|
0dfd0ad4b0 | ||
|
|
e88e11b9ba | ||
|
|
7a2a79ca7b | ||
|
|
4c0683c484 | ||
|
|
dfe62db8ee | ||
|
|
025c824fbb | ||
|
|
930c5d7c7a | ||
|
|
8b8dcc0127 | ||
|
|
4ad8e88bd8 | ||
|
|
89d4640e92 | ||
|
|
60b12bad61 | ||
|
|
2595fa5c51 | ||
|
|
3e487e5f13 | ||
|
|
b6d6c68e54 | ||
|
|
49548d6f9f | ||
|
|
8faadc23b0 | ||
|
|
20da1ebfab | ||
|
|
03305c72c7 | ||
|
|
a636fd1cf0 | ||
|
|
d8797a8dc6 | ||
|
|
363e18e15d | ||
|
|
982cac8c43 | ||
|
|
fabf64b838 | ||
|
|
e3e6ebe953 | ||
|
|
7ad9b52546 | ||
|
|
abb5090d63 | ||
|
|
52ae05d057 | ||
|
|
3cd216d119 | ||
|
|
581edc0a38 | ||
|
|
f17ebbede6 | ||
|
|
2bd2c09c23 | ||
|
|
c1cf859ec9 | ||
|
|
1ebf864998 | ||
|
|
0948e34e48 | ||
|
|
f9a1ea9c83 | ||
|
|
d02a8ebc1b | ||
|
|
5c2625b648 | ||
|
|
13174d006e | ||
|
|
2c59cc4cc3 | ||
|
|
41c81d3226 | ||
|
|
2ec7d3b4d9 | ||
|
|
b607b73598 | ||
|
|
f52791cd1f | ||
|
|
d829facbd4 | ||
|
|
44ac3a9ff2 | ||
|
|
a19302babc | ||
|
|
18a627b01e | ||
|
|
eddb5480e9 | ||
|
|
5b26757662 |
@@ -22,3 +22,7 @@ indent_style = tab
|
|||||||
|
|
||||||
[*.bat]
|
[*.bat]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
|
[{Dockerfile,*.dockerfile}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -38,7 +38,6 @@ htmlcov/
|
|||||||
.tox/
|
.tox/
|
||||||
.coverage
|
.coverage
|
||||||
.cache
|
.cache
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
@@ -77,3 +76,4 @@ celerybeat-schedule
|
|||||||
.flake8
|
.flake8
|
||||||
.pylintrc
|
.pylintrc
|
||||||
Makefile
|
Makefile
|
||||||
|
.isort.cfg
|
||||||
|
|||||||
120
.gitlab-ci.yml
120
.gitlab-ci.yml
@@ -1,8 +1,15 @@
|
|||||||
|
.only-default: &only-default
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- branches
|
||||||
|
- merge_requests
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- pre-commit
|
- pre-commit
|
||||||
- gitlab
|
- gitlab
|
||||||
- test
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
|
- docker
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- template: Dependency-Scanning.gitlab-ci.yml
|
- template: Dependency-Scanning.gitlab-ci.yml
|
||||||
@@ -15,6 +22,7 @@ before_script:
|
|||||||
- pip install wheel tox
|
- pip install wheel tox
|
||||||
|
|
||||||
pre-commit-check:
|
pre-commit-check:
|
||||||
|
<<: *only-default
|
||||||
stage: pre-commit
|
stage: pre-commit
|
||||||
image: python:3.6-buster
|
image: python:3.6-buster
|
||||||
variables:
|
variables:
|
||||||
@@ -39,6 +47,7 @@ dependency_scanning:
|
|||||||
- pip install wheel tox
|
- pip install wheel tox
|
||||||
|
|
||||||
test-3.7-core:
|
test-3.7-core:
|
||||||
|
<<: *only-default
|
||||||
image: python:3.7-bullseye
|
image: python:3.7-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py37-core
|
- tox -e py37-core
|
||||||
@@ -48,6 +57,7 @@ test-3.7-core:
|
|||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.8-core:
|
test-3.8-core:
|
||||||
|
<<: *only-default
|
||||||
image: python:3.8-bullseye
|
image: python:3.8-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py38-core
|
- tox -e py38-core
|
||||||
@@ -57,6 +67,7 @@ test-3.8-core:
|
|||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.9-core:
|
test-3.9-core:
|
||||||
|
<<: *only-default
|
||||||
image: python:3.9-bullseye
|
image: python:3.9-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py39-core
|
- tox -e py39-core
|
||||||
@@ -65,7 +76,29 @@ test-3.9-core:
|
|||||||
reports:
|
reports:
|
||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
|
test-3.10-core:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.10-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e py310-core
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
|
||||||
|
test-3.11-core:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.11-rc-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e py311-core
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
test-3.7-all:
|
test-3.7-all:
|
||||||
|
<<: *only-default
|
||||||
image: python:3.7-bullseye
|
image: python:3.7-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py37-all
|
- tox -e py37-all
|
||||||
@@ -75,6 +108,7 @@ test-3.7-all:
|
|||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.8-all:
|
test-3.8-all:
|
||||||
|
<<: *only-default
|
||||||
image: python:3.8-bullseye
|
image: python:3.8-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py38-all
|
- tox -e py38-all
|
||||||
@@ -84,6 +118,7 @@ test-3.8-all:
|
|||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.9-all:
|
test-3.9-all:
|
||||||
|
<<: *only-default
|
||||||
image: python:3.9-bullseye
|
image: python:3.9-bullseye
|
||||||
script:
|
script:
|
||||||
- tox -e py39-all
|
- tox -e py39-all
|
||||||
@@ -92,9 +127,30 @@ test-3.9-all:
|
|||||||
reports:
|
reports:
|
||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
|
test-3.10-all:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.10-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e py310-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
|
||||||
|
test-3.11-all:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.11-rc-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e py311-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
deploy_production:
|
deploy_production:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: python:3.9-bullseye
|
image: python:3.10-bullseye
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- pip install twine wheel
|
- pip install twine wheel
|
||||||
@@ -105,3 +161,65 @@ deploy_production:
|
|||||||
|
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG
|
- if: $CI_COMMIT_TAG
|
||||||
|
|
||||||
|
build-image:
|
||||||
|
before_script: []
|
||||||
|
image: docker:20.10.10
|
||||||
|
stage: docker
|
||||||
|
services:
|
||||||
|
- docker:20.10.10-dind
|
||||||
|
script: |
|
||||||
|
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
||||||
|
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_SHORT_SHA
|
||||||
|
CURRENT_TAG=$CI_REGISTRY_IMAGE/auth:$CI_COMMIT_TAG
|
||||||
|
MINOR_TAG=$CI_REGISTRY_IMAGE/auth:$(echo $CI_COMMIT_TAG | cut -d '.' -f 1-2)
|
||||||
|
MAJOR_TAG=$CI_REGISTRY_IMAGE/auth:$(echo $CI_COMMIT_TAG | cut -d '.' -f 1)
|
||||||
|
LATEST_TAG=$CI_REGISTRY_IMAGE/auth:latest
|
||||||
|
|
||||||
|
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
docker build . -t $IMAGE_TAG -f docker/Dockerfile --build-arg AUTH_VERSION=$(echo $CI_COMMIT_TAG | cut -c 2-)
|
||||||
|
docker tag $IMAGE_TAG $CURRENT_TAG
|
||||||
|
docker tag $IMAGE_TAG $MINOR_TAG
|
||||||
|
docker tag $IMAGE_TAG $MAJOR_TAG
|
||||||
|
docker tag $IMAGE_TAG $LATEST_TAG
|
||||||
|
docker image push --all-tags $CI_REGISTRY_IMAGE/auth
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
|
||||||
|
build-image-dev:
|
||||||
|
before_script: []
|
||||||
|
image: docker:20.10.10
|
||||||
|
stage: docker
|
||||||
|
services:
|
||||||
|
- docker:20.10.10-dind
|
||||||
|
script: |
|
||||||
|
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
||||||
|
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
|
||||||
|
|
||||||
|
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
docker build . -t $IMAGE_TAG -f docker/Dockerfile --build-arg AUTH_PACKAGE=git+https://gitlab.com/allianceauth/allianceauth@$CI_COMMIT_BRANCH
|
||||||
|
docker push $IMAGE_TAG
|
||||||
|
rules:
|
||||||
|
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == ""'
|
||||||
|
when: manual
|
||||||
|
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME != ""'
|
||||||
|
when: never
|
||||||
|
|
||||||
|
build-image-mr:
|
||||||
|
before_script: []
|
||||||
|
image: docker:20.10.10
|
||||||
|
stage: docker
|
||||||
|
services:
|
||||||
|
- docker:20.10.10-dind
|
||||||
|
script: |
|
||||||
|
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
||||||
|
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA
|
||||||
|
|
||||||
|
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
docker build . -t $IMAGE_TAG -f docker/Dockerfile --build-arg AUTH_PACKAGE=git+$CI_MERGE_REQUEST_SOURCE_PROJECT_URL@$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
|
||||||
|
docker push $IMAGE_TAG
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
when: manual
|
||||||
|
- if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
|
||||||
|
when: never
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# This will make sure the app is always imported when
|
# This will make sure the app is always imported when
|
||||||
# Django starts so that shared_task will use this app.
|
# Django starts so that shared_task will use this app.
|
||||||
|
|
||||||
__version__ = '2.9.1'
|
__version__ = '2.11.2'
|
||||||
__title__ = 'Alliance Auth'
|
__title__ = 'Alliance Auth'
|
||||||
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
||||||
NAME = f'{__title__} v{__version__}'
|
NAME = f'{__title__} v{__version__}'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
from .models import AnalyticsTokens, AnalyticsIdentifier
|
from .models import AnalyticsTokens, AnalyticsIdentifier
|
||||||
from .tasks import send_ga_tracking_web_view
|
from .tasks import send_ga_tracking_web_view
|
||||||
@@ -10,6 +11,8 @@ import re
|
|||||||
class AnalyticsMiddleware(MiddlewareMixin):
|
class AnalyticsMiddleware(MiddlewareMixin):
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
"""Django Middleware: Process Page Views and creates Analytics Celery Tasks"""
|
"""Django Middleware: Process Page Views and creates Analytics Celery Tasks"""
|
||||||
|
if getattr(settings, "ANALYTICS_DISABLED", False):
|
||||||
|
return response
|
||||||
analyticstokens = AnalyticsTokens.objects.all()
|
analyticstokens = AnalyticsTokens.objects.all()
|
||||||
client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex
|
client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Generated by Django 3.1.13 on 2021-10-15 05:02
|
# Generated by Django 3.1.13 on 2021-10-15 05:02
|
||||||
|
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def modify_aa_team_token_add_page_ignore_paths(apps, schema_editor):
|
def modify_aa_team_token_add_page_ignore_paths(apps, schema_editor):
|
||||||
# We can't import the Person model directly as it may be a newer
|
# Add /admin/ and /user_notifications_count/ path to ignore
|
||||||
# version than this migration expects. We use the historical version.
|
|
||||||
|
|
||||||
AnalyticsPath = apps.get_model('analytics', 'AnalyticsPath')
|
AnalyticsPath = apps.get_model('analytics', 'AnalyticsPath')
|
||||||
admin = AnalyticsPath.objects.create(ignore_path=r"^\/admin\/.*")
|
admin = AnalyticsPath.objects.create(ignore_path=r"^\/admin\/.*")
|
||||||
@@ -17,8 +17,19 @@ def modify_aa_team_token_add_page_ignore_paths(apps, schema_editor):
|
|||||||
|
|
||||||
|
|
||||||
def undo_modify_aa_team_token_add_page_ignore_paths(apps, schema_editor):
|
def undo_modify_aa_team_token_add_page_ignore_paths(apps, schema_editor):
|
||||||
# nothing should need to migrate away here?
|
#
|
||||||
return True
|
AnalyticsPath = apps.get_model('analytics', 'AnalyticsPath')
|
||||||
|
Tokens = apps.get_model('analytics', 'AnalyticsTokens')
|
||||||
|
|
||||||
|
token = Tokens.objects.get(token="UA-186249766-2")
|
||||||
|
try:
|
||||||
|
admin = AnalyticsPath.objects.get(ignore_path=r"^\/admin\/.*", analyticstokens=token)
|
||||||
|
user_notifications_count = AnalyticsPath.objects.get(ignore_path=r"^\/user_notifications_count\/.*", analyticstokens=token)
|
||||||
|
admin.delete()
|
||||||
|
user_notifications_count.delete()
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
# Its fine if it doesnt exist, we just dont want them building up when re-migrating
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|||||||
40
allianceauth/analytics/migrations/0006_more_ignore_paths.py
Normal file
40
allianceauth/analytics/migrations/0006_more_ignore_paths.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Generated by Django 3.2.8 on 2021-10-19 01:47
|
||||||
|
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def modify_aa_team_token_add_page_ignore_paths(apps, schema_editor):
|
||||||
|
# Add the /account/activate path to ignore
|
||||||
|
|
||||||
|
AnalyticsPath = apps.get_model('analytics', 'AnalyticsPath')
|
||||||
|
account_activate = AnalyticsPath.objects.create(ignore_path=r"^\/account\/activate\/.*")
|
||||||
|
|
||||||
|
Tokens = apps.get_model('analytics', 'AnalyticsTokens')
|
||||||
|
token = Tokens.objects.get(token="UA-186249766-2")
|
||||||
|
token.ignore_paths.add(account_activate)
|
||||||
|
|
||||||
|
|
||||||
|
def undo_modify_aa_team_token_add_page_ignore_paths(apps, schema_editor):
|
||||||
|
#
|
||||||
|
AnalyticsPath = apps.get_model('analytics', 'AnalyticsPath')
|
||||||
|
Tokens = apps.get_model('analytics', 'AnalyticsTokens')
|
||||||
|
|
||||||
|
token = Tokens.objects.get(token="UA-186249766-2")
|
||||||
|
|
||||||
|
try:
|
||||||
|
account_activate = AnalyticsPath.objects.get(ignore_path=r"^\/account\/activate\/.*", analyticstokens=token)
|
||||||
|
account_activate.delete()
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
# Its fine if it doesnt exist, we just dont want them building up when re-migrating
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('analytics', '0005_alter_analyticspath_ignore_path'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(modify_aa_team_token_add_page_ignore_paths, undo_modify_aa_team_token_add_page_ignore_paths)
|
||||||
|
]
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
from allianceauth.analytics.tasks import analytics_event
|
|
||||||
from celery.signals import task_failure, task_success
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from celery.signals import task_failure, task_success
|
||||||
|
from django.conf import settings
|
||||||
|
from allianceauth.analytics.tasks import analytics_event
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -11,6 +12,8 @@ def process_failure_signal(
|
|||||||
sender, task_id, signal,
|
sender, task_id, signal,
|
||||||
args, kwargs, einfo, **kw):
|
args, kwargs, einfo, **kw):
|
||||||
logger.debug("Celery task_failure signal %s" % sender.__class__.__name__)
|
logger.debug("Celery task_failure signal %s" % sender.__class__.__name__)
|
||||||
|
if getattr(settings, "ANALYTICS_DISABLED", False):
|
||||||
|
return
|
||||||
|
|
||||||
category = sender.__module__
|
category = sender.__module__
|
||||||
|
|
||||||
@@ -30,6 +33,8 @@ def process_failure_signal(
|
|||||||
@task_success.connect
|
@task_success.connect
|
||||||
def celery_success_signal(sender, result=None, **kw):
|
def celery_success_signal(sender, result=None, **kw):
|
||||||
logger.debug("Celery task_success signal %s" % sender.__class__.__name__)
|
logger.debug("Celery task_success signal %s" % sender.__class__.__name__)
|
||||||
|
if getattr(settings, "ANALYTICS_DISABLED", False):
|
||||||
|
return
|
||||||
|
|
||||||
category = sender.__module__
|
category = sender.__module__
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ if getattr(settings, "ANALYTICS_ENABLE_DEBUG", False) and settings.DEBUG:
|
|||||||
# Force sending of analytics data during in a debug/test environemt
|
# Force sending of analytics data during in a debug/test environemt
|
||||||
# Usefull for developers working on this feature.
|
# Usefull for developers working on this feature.
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"You have 'ANALYTICS_ENABLE_DEBUG' Enabled! "
|
"You have 'ANALYTICS_ENABLE_DEBUG' Enabled! "
|
||||||
"This debug instance will send analytics data!")
|
"This debug instance will send analytics data!")
|
||||||
DEBUG_URL = COLLECTION_URL
|
DEBUG_URL = COLLECTION_URL
|
||||||
|
|
||||||
ANALYTICS_URL = COLLECTION_URL
|
ANALYTICS_URL = COLLECTION_URL
|
||||||
@@ -40,13 +40,12 @@ def analytics_event(category: str,
|
|||||||
Send a Google Analytics Event for each token stored
|
Send a Google Analytics Event for each token stored
|
||||||
Includes check for if its enabled/disabled
|
Includes check for if its enabled/disabled
|
||||||
|
|
||||||
Parameters
|
Args:
|
||||||
-------
|
`category` (str): Celery Namespace
|
||||||
`category` (str): Celery Namespace
|
`action` (str): Task Name
|
||||||
`action` (str): Task Name
|
`label` (str): Optional, Task Success/Exception
|
||||||
`label` (str): Optional, Task Success/Exception
|
`value` (int): Optional, If bulk, Query size, can be a binary True/False
|
||||||
`value` (int): Optional, If bulk, Query size, can be a binary True/False
|
`event_type` (str): Optional, Celery or Stats only, Default to Celery
|
||||||
`event_type` (str): Optional, Celery or Stats only, Default to Celery
|
|
||||||
"""
|
"""
|
||||||
analyticstokens = AnalyticsTokens.objects.all()
|
analyticstokens = AnalyticsTokens.objects.all()
|
||||||
client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex
|
client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex
|
||||||
@@ -60,20 +59,21 @@ def analytics_event(category: str,
|
|||||||
|
|
||||||
if allowed is True:
|
if allowed is True:
|
||||||
tracking_id = token.token
|
tracking_id = token.token
|
||||||
send_ga_tracking_celery_event.s(tracking_id=tracking_id,
|
send_ga_tracking_celery_event.s(
|
||||||
client_id=client_id,
|
tracking_id=tracking_id,
|
||||||
category=category,
|
client_id=client_id,
|
||||||
action=action,
|
category=category,
|
||||||
label=label,
|
action=action,
|
||||||
value=value).\
|
label=label,
|
||||||
apply_async(priority=9)
|
value=value).apply_async(priority=9)
|
||||||
|
|
||||||
|
|
||||||
@shared_task()
|
@shared_task()
|
||||||
def analytics_daily_stats():
|
def analytics_daily_stats():
|
||||||
"""Celery Task: Do not call directly
|
"""Celery Task: Do not call directly
|
||||||
|
|
||||||
Gathers a series of daily statistics and sends analytics events containing them"""
|
Gathers a series of daily statistics and sends analytics events containing them
|
||||||
|
"""
|
||||||
users = install_stat_users()
|
users = install_stat_users()
|
||||||
tokens = install_stat_tokens()
|
tokens = install_stat_tokens()
|
||||||
addons = install_stat_addons()
|
addons = install_stat_addons()
|
||||||
|
|||||||
108
allianceauth/analytics/tests/test_integration.py
Normal file
108
allianceauth/analytics/tests/test_integration.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
from urllib.parse import parse_qs
|
||||||
|
|
||||||
|
import requests_mock
|
||||||
|
|
||||||
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
|
from allianceauth.analytics.tasks import ANALYTICS_URL
|
||||||
|
from allianceauth.eveonline.tasks import update_character
|
||||||
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||||
|
@requests_mock.mock()
|
||||||
|
class TestAnalyticsForViews(TestCase):
|
||||||
|
@override_settings(ANALYTICS_DISABLED=False)
|
||||||
|
def test_should_run_analytics(self, requests_mocker):
|
||||||
|
# given
|
||||||
|
requests_mocker.post(ANALYTICS_URL)
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
self.client.force_login(user)
|
||||||
|
# when
|
||||||
|
response = self.client.get("/dashboard/")
|
||||||
|
# then
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertTrue(requests_mocker.called)
|
||||||
|
|
||||||
|
@override_settings(ANALYTICS_DISABLED=True)
|
||||||
|
def test_should_not_run_analytics(self, requests_mocker):
|
||||||
|
# given
|
||||||
|
requests_mocker.post(ANALYTICS_URL)
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
self.client.force_login(user)
|
||||||
|
# when
|
||||||
|
response = self.client.get("/dashboard/")
|
||||||
|
# then
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertFalse(requests_mocker.called)
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||||
|
@requests_mock.mock()
|
||||||
|
class TestAnalyticsForTasks(TestCase):
|
||||||
|
@override_settings(ANALYTICS_DISABLED=False)
|
||||||
|
@patch("allianceauth.eveonline.models.EveCharacter.objects.update_character")
|
||||||
|
def test_should_run_analytics_for_successful_task(
|
||||||
|
self, requests_mocker, mock_update_character
|
||||||
|
):
|
||||||
|
# given
|
||||||
|
requests_mocker.post(ANALYTICS_URL)
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001)
|
||||||
|
# when
|
||||||
|
update_character.delay(character.character_id)
|
||||||
|
# then
|
||||||
|
self.assertTrue(mock_update_character.called)
|
||||||
|
self.assertTrue(requests_mocker.called)
|
||||||
|
payload = parse_qs(requests_mocker.last_request.text)
|
||||||
|
self.assertListEqual(payload["el"], ["Success"])
|
||||||
|
|
||||||
|
@override_settings(ANALYTICS_DISABLED=True)
|
||||||
|
@patch("allianceauth.eveonline.models.EveCharacter.objects.update_character")
|
||||||
|
def test_should_not_run_analytics_for_successful_task(
|
||||||
|
self, requests_mocker, mock_update_character
|
||||||
|
):
|
||||||
|
# given
|
||||||
|
requests_mocker.post(ANALYTICS_URL)
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001)
|
||||||
|
# when
|
||||||
|
update_character.delay(character.character_id)
|
||||||
|
# then
|
||||||
|
self.assertTrue(mock_update_character.called)
|
||||||
|
self.assertFalse(requests_mocker.called)
|
||||||
|
|
||||||
|
@override_settings(ANALYTICS_DISABLED=False)
|
||||||
|
@patch("allianceauth.eveonline.models.EveCharacter.objects.update_character")
|
||||||
|
def test_should_run_analytics_for_failed_task(
|
||||||
|
self, requests_mocker, mock_update_character
|
||||||
|
):
|
||||||
|
# given
|
||||||
|
requests_mocker.post(ANALYTICS_URL)
|
||||||
|
mock_update_character.side_effect = RuntimeError
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001)
|
||||||
|
# when
|
||||||
|
update_character.delay(character.character_id)
|
||||||
|
# then
|
||||||
|
self.assertTrue(mock_update_character.called)
|
||||||
|
self.assertTrue(requests_mocker.called)
|
||||||
|
payload = parse_qs(requests_mocker.last_request.text)
|
||||||
|
self.assertNotEqual(payload["el"], ["Success"])
|
||||||
|
|
||||||
|
@override_settings(ANALYTICS_DISABLED=True)
|
||||||
|
@patch("allianceauth.eveonline.models.EveCharacter.objects.update_character")
|
||||||
|
def test_should_not_run_analytics_for_failed_task(
|
||||||
|
self, requests_mocker, mock_update_character
|
||||||
|
):
|
||||||
|
# given
|
||||||
|
requests_mocker.post(ANALYTICS_URL)
|
||||||
|
mock_update_character.side_effect = RuntimeError
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001)
|
||||||
|
# when
|
||||||
|
update_character.delay(character.character_id)
|
||||||
|
# then
|
||||||
|
self.assertTrue(mock_update_character.called)
|
||||||
|
self.assertFalse(requests_mocker.called)
|
||||||
@@ -380,6 +380,7 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
'username',
|
'username',
|
||||||
'character_ownerships__character__character_name'
|
'character_ownerships__character__character_name'
|
||||||
)
|
)
|
||||||
|
readonly_fields = ('date_joined', 'last_login')
|
||||||
|
|
||||||
def _characters(self, obj):
|
def _characters(self, obj):
|
||||||
character_ownerships = list(obj.character_ownerships.all())
|
character_ownerships = list(obj.character_ownerships.all())
|
||||||
@@ -425,10 +426,19 @@ class UserAdmin(BaseUserAdmin):
|
|||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
return request.user.has_perm('auth.delete_user')
|
return request.user.has_perm('auth.delete_user')
|
||||||
|
|
||||||
|
def get_object(self, *args , **kwargs):
|
||||||
|
obj = super().get_object(*args , **kwargs)
|
||||||
|
self.obj = obj # storing current object for use in formfield_for_manytomany
|
||||||
|
return obj
|
||||||
|
|
||||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
"""overriding this formfield to have sorted lists in the form"""
|
|
||||||
if db_field.name == "groups":
|
if db_field.name == "groups":
|
||||||
kwargs["queryset"] = Group.objects.all().order_by(Lower('name'))
|
groups_qs = Group.objects.filter(authgroup__states__isnull=True)
|
||||||
|
obj_state = self.obj.profile.state
|
||||||
|
if obj_state:
|
||||||
|
matching_groups_qs = Group.objects.filter(authgroup__states=obj_state)
|
||||||
|
groups_qs = groups_qs | matching_groups_qs
|
||||||
|
kwargs["queryset"] = groups_qs.order_by(Lower('name'))
|
||||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,14 @@ from django.core.checks import register, Tags
|
|||||||
|
|
||||||
|
|
||||||
class AuthenticationConfig(AppConfig):
|
class AuthenticationConfig(AppConfig):
|
||||||
name = 'allianceauth.authentication'
|
name = "allianceauth.authentication"
|
||||||
label = 'authentication'
|
label = "authentication"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
super().ready()
|
from allianceauth.authentication import checks, signals # noqa: F401
|
||||||
from allianceauth.authentication import checks, signals
|
from allianceauth.authentication.task_statistics import (
|
||||||
|
signals as celery_signals,
|
||||||
|
)
|
||||||
|
|
||||||
register(Tags.security)(checks.check_login_scopes_setting)
|
register(Tags.security)(checks.check_login_scopes_setting)
|
||||||
|
celery_signals.reset_counters()
|
||||||
|
|||||||
40
allianceauth/authentication/task_statistics/counters.py
Normal file
40
allianceauth/authentication/task_statistics/counters.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
from .event_series import EventSeries
|
||||||
|
|
||||||
|
|
||||||
|
"""Global series for counting task events."""
|
||||||
|
succeeded_tasks = EventSeries("SUCCEEDED_TASKS")
|
||||||
|
retried_tasks = EventSeries("RETRIED_TASKS")
|
||||||
|
failed_tasks = EventSeries("FAILED_TASKS")
|
||||||
|
|
||||||
|
|
||||||
|
_TaskCounts = namedtuple(
|
||||||
|
"_TaskCounts", ["succeeded", "retried", "failed", "total", "earliest_task", "hours"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dashboard_results(hours: int) -> _TaskCounts:
|
||||||
|
"""Counts of all task events within the given timeframe."""
|
||||||
|
|
||||||
|
def earliest_if_exists(events: EventSeries, earliest: dt.datetime) -> list:
|
||||||
|
my_earliest = events.first_event(earliest=earliest)
|
||||||
|
return [my_earliest] if my_earliest else []
|
||||||
|
|
||||||
|
earliest = dt.datetime.utcnow() - dt.timedelta(hours=hours)
|
||||||
|
earliest_events = list()
|
||||||
|
succeeded_count = succeeded_tasks.count(earliest=earliest)
|
||||||
|
earliest_events += earliest_if_exists(succeeded_tasks, earliest)
|
||||||
|
retried_count = retried_tasks.count(earliest=earliest)
|
||||||
|
earliest_events += earliest_if_exists(retried_tasks, earliest)
|
||||||
|
failed_count = failed_tasks.count(earliest=earliest)
|
||||||
|
earliest_events += earliest_if_exists(failed_tasks, earliest)
|
||||||
|
return _TaskCounts(
|
||||||
|
succeeded=succeeded_count,
|
||||||
|
retried=retried_count,
|
||||||
|
failed=failed_count,
|
||||||
|
total=succeeded_count + retried_count + failed_count,
|
||||||
|
earliest_task=min(earliest_events) if earliest_events else None,
|
||||||
|
hours=hours,
|
||||||
|
)
|
||||||
95
allianceauth/authentication/task_statistics/event_series.py
Normal file
95
allianceauth/authentication/task_statistics/event_series.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import datetime as dt
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from redis import Redis
|
||||||
|
from pytz import utc
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
|
|
||||||
|
|
||||||
|
class EventSeries:
|
||||||
|
"""API for recording and analysing a series of events."""
|
||||||
|
|
||||||
|
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
|
||||||
|
|
||||||
|
def __init__(self, key_id: str, redis: Redis = None) -> None:
|
||||||
|
self._redis = cache.get_master_client() if not redis else redis
|
||||||
|
if not isinstance(self._redis, Redis):
|
||||||
|
raise TypeError(
|
||||||
|
"This class requires a Redis client, but none was provided "
|
||||||
|
"and the default Django cache backend is not Redis either."
|
||||||
|
)
|
||||||
|
self._key_id = str(key_id)
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _key_counter(self):
|
||||||
|
return f"{self._ROOT_KEY}_{self._key_id}_COUNTER"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _key_sorted_set(self):
|
||||||
|
return f"{self._ROOT_KEY}_{self._key_id}_SORTED_SET"
|
||||||
|
|
||||||
|
def add(self, event_time: dt.datetime = None) -> None:
|
||||||
|
"""Add event.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- event_time: timestamp of event. Will use current time if not specified.
|
||||||
|
"""
|
||||||
|
if not event_time:
|
||||||
|
event_time = dt.datetime.utcnow()
|
||||||
|
id = self._redis.incr(self._key_counter)
|
||||||
|
self._redis.zadd(self._key_sorted_set, {id: event_time.timestamp()})
|
||||||
|
|
||||||
|
def all(self) -> List[dt.datetime]:
|
||||||
|
"""List of all known events."""
|
||||||
|
return [
|
||||||
|
event[1]
|
||||||
|
for event in self._redis.zrangebyscore(
|
||||||
|
self._key_sorted_set,
|
||||||
|
"-inf",
|
||||||
|
"+inf",
|
||||||
|
withscores=True,
|
||||||
|
score_cast_func=self._cast_scores_to_dt,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""Clear all events."""
|
||||||
|
self._redis.delete(self._key_sorted_set)
|
||||||
|
self._redis.delete(self._key_counter)
|
||||||
|
|
||||||
|
def count(self, earliest: dt.datetime = None, latest: dt.datetime = None) -> int:
|
||||||
|
"""Count of events, can be restricted to given timeframe.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- earliest: Date of first events to count(inclusive), or -infinite if not specified
|
||||||
|
- latest: Date of last events to count(inclusive), or +infinite if not specified
|
||||||
|
"""
|
||||||
|
min = "-inf" if not earliest else earliest.timestamp()
|
||||||
|
max = "+inf" if not latest else latest.timestamp()
|
||||||
|
return self._redis.zcount(self._key_sorted_set, min=min, max=max)
|
||||||
|
|
||||||
|
def first_event(self, earliest: dt.datetime = None) -> Optional[dt.datetime]:
|
||||||
|
"""Date/Time of first event. Returns `None` if series has no events.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- earliest: Date of first events to count(inclusive), or any if not specified
|
||||||
|
"""
|
||||||
|
min = "-inf" if not earliest else earliest.timestamp()
|
||||||
|
event = self._redis.zrangebyscore(
|
||||||
|
self._key_sorted_set,
|
||||||
|
min,
|
||||||
|
"+inf",
|
||||||
|
withscores=True,
|
||||||
|
start=0,
|
||||||
|
num=1,
|
||||||
|
score_cast_func=self._cast_scores_to_dt,
|
||||||
|
)
|
||||||
|
if not event:
|
||||||
|
return None
|
||||||
|
return event[0][1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _cast_scores_to_dt(score) -> dt.datetime:
|
||||||
|
return dt.datetime.fromtimestamp(float(score), tz=utc)
|
||||||
54
allianceauth/authentication/task_statistics/signals.py
Normal file
54
allianceauth/authentication/task_statistics/signals.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from celery.signals import (
|
||||||
|
task_failure,
|
||||||
|
task_internal_error,
|
||||||
|
task_retry,
|
||||||
|
task_success,
|
||||||
|
worker_ready
|
||||||
|
)
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from .counters import failed_tasks, retried_tasks, succeeded_tasks
|
||||||
|
|
||||||
|
|
||||||
|
def reset_counters():
|
||||||
|
"""Reset all counters for the celery status."""
|
||||||
|
succeeded_tasks.clear()
|
||||||
|
failed_tasks.clear()
|
||||||
|
retried_tasks.clear()
|
||||||
|
|
||||||
|
|
||||||
|
def is_enabled() -> bool:
|
||||||
|
return not bool(
|
||||||
|
getattr(settings, "ALLIANCEAUTH_DASHBOARD_TASK_STATISTICS_DISABLED", False)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@worker_ready.connect
|
||||||
|
def reset_counters_when_celery_restarted(*args, **kwargs):
|
||||||
|
if is_enabled():
|
||||||
|
reset_counters()
|
||||||
|
|
||||||
|
|
||||||
|
@task_success.connect
|
||||||
|
def record_task_succeeded(*args, **kwargs):
|
||||||
|
if is_enabled():
|
||||||
|
succeeded_tasks.add()
|
||||||
|
|
||||||
|
|
||||||
|
@task_retry.connect
|
||||||
|
def record_task_retried(*args, **kwargs):
|
||||||
|
if is_enabled():
|
||||||
|
retried_tasks.add()
|
||||||
|
|
||||||
|
|
||||||
|
@task_failure.connect
|
||||||
|
def record_task_failed(*args, **kwargs):
|
||||||
|
if is_enabled():
|
||||||
|
failed_tasks.add()
|
||||||
|
|
||||||
|
|
||||||
|
@task_internal_error.connect
|
||||||
|
def record_task_internal_error(*args, **kwargs):
|
||||||
|
if is_enabled():
|
||||||
|
failed_tasks.add()
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
from allianceauth.authentication.task_statistics.counters import (
|
||||||
|
dashboard_results,
|
||||||
|
succeeded_tasks,
|
||||||
|
retried_tasks,
|
||||||
|
failed_tasks,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDashboardResults(TestCase):
|
||||||
|
def test_should_return_counts_for_given_timeframe_only(self):
|
||||||
|
# given
|
||||||
|
earliest_task = now() - dt.timedelta(minutes=15)
|
||||||
|
succeeded_tasks.clear()
|
||||||
|
succeeded_tasks.add(now() - dt.timedelta(hours=1, seconds=1))
|
||||||
|
succeeded_tasks.add(earliest_task)
|
||||||
|
succeeded_tasks.add()
|
||||||
|
succeeded_tasks.add()
|
||||||
|
retried_tasks.clear()
|
||||||
|
retried_tasks.add(now() - dt.timedelta(hours=1, seconds=1))
|
||||||
|
retried_tasks.add(now() - dt.timedelta(seconds=30))
|
||||||
|
retried_tasks.add()
|
||||||
|
failed_tasks.clear()
|
||||||
|
failed_tasks.add(now() - dt.timedelta(hours=1, seconds=1))
|
||||||
|
failed_tasks.add()
|
||||||
|
# when
|
||||||
|
results = dashboard_results(hours=1)
|
||||||
|
# then
|
||||||
|
self.assertEqual(results.succeeded, 3)
|
||||||
|
self.assertEqual(results.retried, 2)
|
||||||
|
self.assertEqual(results.failed, 1)
|
||||||
|
self.assertEqual(results.total, 6)
|
||||||
|
self.assertEqual(results.earliest_task, earliest_task)
|
||||||
|
|
||||||
|
def test_should_work_with_no_data(self):
|
||||||
|
# given
|
||||||
|
succeeded_tasks.clear()
|
||||||
|
retried_tasks.clear()
|
||||||
|
failed_tasks.clear()
|
||||||
|
# when
|
||||||
|
results = dashboard_results(hours=1)
|
||||||
|
# then
|
||||||
|
self.assertEqual(results.succeeded, 0)
|
||||||
|
self.assertEqual(results.retried, 0)
|
||||||
|
self.assertEqual(results.failed, 0)
|
||||||
|
self.assertEqual(results.total, 0)
|
||||||
|
self.assertIsNone(results.earliest_task)
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
from pytz import utc
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
from allianceauth.authentication.task_statistics.event_series import EventSeries
|
||||||
|
|
||||||
|
|
||||||
|
class TestEventSeries(TestCase):
|
||||||
|
def test_should_add_event(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
# when
|
||||||
|
events.add()
|
||||||
|
# then
|
||||||
|
result = events.all()
|
||||||
|
self.assertEqual(len(result), 1)
|
||||||
|
self.assertAlmostEqual(result[0], now(), delta=dt.timedelta(seconds=30))
|
||||||
|
|
||||||
|
def test_should_add_event_with_specified_time(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
my_time = dt.datetime(2021, 11, 1, 12, 15, tzinfo=utc)
|
||||||
|
# when
|
||||||
|
events.add(my_time)
|
||||||
|
# then
|
||||||
|
result = events.all()
|
||||||
|
self.assertEqual(len(result), 1)
|
||||||
|
self.assertAlmostEqual(result[0], my_time, delta=dt.timedelta(seconds=30))
|
||||||
|
|
||||||
|
def test_should_count_events(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add()
|
||||||
|
events.add()
|
||||||
|
# when
|
||||||
|
result = events.count()
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, 2)
|
||||||
|
|
||||||
|
def test_should_count_zero(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
# when
|
||||||
|
result = events.count()
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, 0)
|
||||||
|
|
||||||
|
def test_should_count_events_within_timeframe_1(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 0, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 10, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 15, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 30, tzinfo=utc))
|
||||||
|
# when
|
||||||
|
result = events.count(
|
||||||
|
earliest=dt.datetime(2021, 12, 1, 12, 8, tzinfo=utc),
|
||||||
|
latest=dt.datetime(2021, 12, 1, 12, 17, tzinfo=utc),
|
||||||
|
)
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, 2)
|
||||||
|
|
||||||
|
def test_should_count_events_within_timeframe_2(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 0, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 10, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 15, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 30, tzinfo=utc))
|
||||||
|
# when
|
||||||
|
result = events.count(earliest=dt.datetime(2021, 12, 1, 12, 8))
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, 3)
|
||||||
|
|
||||||
|
def test_should_count_events_within_timeframe_3(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 0, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 10, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 15, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 30, tzinfo=utc))
|
||||||
|
# when
|
||||||
|
result = events.count(latest=dt.datetime(2021, 12, 1, 12, 12))
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, 2)
|
||||||
|
|
||||||
|
def test_should_clear_events(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add()
|
||||||
|
events.add()
|
||||||
|
# when
|
||||||
|
events.clear()
|
||||||
|
# then
|
||||||
|
self.assertEqual(events.count(), 0)
|
||||||
|
|
||||||
|
def test_should_return_date_of_first_event(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 0, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 10, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 15, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 30, tzinfo=utc))
|
||||||
|
# when
|
||||||
|
result = events.first_event()
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, dt.datetime(2021, 12, 1, 12, 0, tzinfo=utc))
|
||||||
|
|
||||||
|
def test_should_return_date_of_first_event_with_range(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 0, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 10, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 15, tzinfo=utc))
|
||||||
|
events.add(dt.datetime(2021, 12, 1, 12, 30, tzinfo=utc))
|
||||||
|
# when
|
||||||
|
result = events.first_event(
|
||||||
|
earliest=dt.datetime(2021, 12, 1, 12, 8, tzinfo=utc)
|
||||||
|
)
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, dt.datetime(2021, 12, 1, 12, 10, tzinfo=utc))
|
||||||
|
|
||||||
|
def test_should_return_all_events(self):
|
||||||
|
# given
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
events.add()
|
||||||
|
events.add()
|
||||||
|
# when
|
||||||
|
results = events.all()
|
||||||
|
# then
|
||||||
|
self.assertEqual(len(results), 2)
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from celery.exceptions import Retry
|
||||||
|
|
||||||
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
|
from allianceauth.authentication.task_statistics.counters import (
|
||||||
|
failed_tasks,
|
||||||
|
retried_tasks,
|
||||||
|
succeeded_tasks,
|
||||||
|
)
|
||||||
|
from allianceauth.authentication.task_statistics.signals import (
|
||||||
|
reset_counters,
|
||||||
|
is_enabled,
|
||||||
|
)
|
||||||
|
from allianceauth.eveonline.tasks import update_character
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
CELERY_ALWAYS_EAGER=True,ALLIANCEAUTH_DASHBOARD_TASK_STATISTICS_DISABLED=False
|
||||||
|
)
|
||||||
|
class TestTaskSignals(TestCase):
|
||||||
|
fixtures = ["disable_analytics"]
|
||||||
|
|
||||||
|
def test_should_record_successful_task(self):
|
||||||
|
# given
|
||||||
|
succeeded_tasks.clear()
|
||||||
|
retried_tasks.clear()
|
||||||
|
failed_tasks.clear()
|
||||||
|
# when
|
||||||
|
with patch(
|
||||||
|
"allianceauth.eveonline.tasks.EveCharacter.objects.update_character"
|
||||||
|
) as mock_update:
|
||||||
|
mock_update.return_value = None
|
||||||
|
update_character.delay(1)
|
||||||
|
# then
|
||||||
|
self.assertEqual(succeeded_tasks.count(), 1)
|
||||||
|
self.assertEqual(retried_tasks.count(), 0)
|
||||||
|
self.assertEqual(failed_tasks.count(), 0)
|
||||||
|
|
||||||
|
def test_should_record_retried_task(self):
|
||||||
|
# given
|
||||||
|
succeeded_tasks.clear()
|
||||||
|
retried_tasks.clear()
|
||||||
|
failed_tasks.clear()
|
||||||
|
# when
|
||||||
|
with patch(
|
||||||
|
"allianceauth.eveonline.tasks.EveCharacter.objects.update_character"
|
||||||
|
) as mock_update:
|
||||||
|
mock_update.side_effect = Retry
|
||||||
|
update_character.delay(1)
|
||||||
|
# then
|
||||||
|
self.assertEqual(succeeded_tasks.count(), 0)
|
||||||
|
self.assertEqual(failed_tasks.count(), 0)
|
||||||
|
self.assertEqual(retried_tasks.count(), 1)
|
||||||
|
|
||||||
|
def test_should_record_failed_task(self):
|
||||||
|
# given
|
||||||
|
succeeded_tasks.clear()
|
||||||
|
retried_tasks.clear()
|
||||||
|
failed_tasks.clear()
|
||||||
|
# when
|
||||||
|
with patch(
|
||||||
|
"allianceauth.eveonline.tasks.EveCharacter.objects.update_character"
|
||||||
|
) as mock_update:
|
||||||
|
mock_update.side_effect = RuntimeError
|
||||||
|
update_character.delay(1)
|
||||||
|
# then
|
||||||
|
self.assertEqual(succeeded_tasks.count(), 0)
|
||||||
|
self.assertEqual(retried_tasks.count(), 0)
|
||||||
|
self.assertEqual(failed_tasks.count(), 1)
|
||||||
|
|
||||||
|
def test_should_reset_counters(self):
|
||||||
|
# given
|
||||||
|
succeeded_tasks.add()
|
||||||
|
retried_tasks.add()
|
||||||
|
failed_tasks.add()
|
||||||
|
# when
|
||||||
|
reset_counters()
|
||||||
|
# then
|
||||||
|
self.assertEqual(succeeded_tasks.count(), 0)
|
||||||
|
self.assertEqual(retried_tasks.count(), 0)
|
||||||
|
self.assertEqual(failed_tasks.count(), 0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestIsEnabled(TestCase):
|
||||||
|
@override_settings(ALLIANCEAUTH_DASHBOARD_TASK_STATISTICS_DISABLED=False)
|
||||||
|
def test_enabled(self):
|
||||||
|
self.assertTrue(is_enabled())
|
||||||
|
|
||||||
|
@override_settings(ALLIANCEAUTH_DASHBOARD_TASK_STATISTICS_DISABLED=True)
|
||||||
|
def test_disabled(self):
|
||||||
|
self.assertFalse(is_enabled())
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from bs4 import BeautifulSoup
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
@@ -188,7 +189,7 @@ class TestCaseWithTestData(TestCase):
|
|||||||
corporation_id=5432,
|
corporation_id=5432,
|
||||||
corporation_name="Xavier's School for Gifted Youngsters",
|
corporation_name="Xavier's School for Gifted Youngsters",
|
||||||
corporation_ticker='MUTNT',
|
corporation_ticker='MUTNT',
|
||||||
alliance_id = None,
|
alliance_id=None,
|
||||||
faction_id=999,
|
faction_id=999,
|
||||||
faction_name='The X-Men',
|
faction_name='The X-Men',
|
||||||
)
|
)
|
||||||
@@ -206,6 +207,7 @@ class TestCaseWithTestData(TestCase):
|
|||||||
cls.user_4.profile.save()
|
cls.user_4.profile.save()
|
||||||
EveFactionInfo.objects.create(faction_id=999, faction_name='The X-Men')
|
EveFactionInfo.objects.create(faction_id=999, faction_name='The X-Men')
|
||||||
|
|
||||||
|
|
||||||
def make_generic_search_request(ModelClass: type, search_term: str):
|
def make_generic_search_request(ModelClass: type, search_term: str):
|
||||||
User.objects.create_superuser(
|
User.objects.create_superuser(
|
||||||
username='superuser', password='secret', email='admin@example.com'
|
username='superuser', password='secret', email='admin@example.com'
|
||||||
@@ -218,6 +220,7 @@ def make_generic_search_request(ModelClass: type, search_term: str):
|
|||||||
|
|
||||||
|
|
||||||
class TestCharacterOwnershipAdmin(TestCaseWithTestData):
|
class TestCharacterOwnershipAdmin(TestCaseWithTestData):
|
||||||
|
fixtures = ["disable_analytics"]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.modeladmin = CharacterOwnershipAdmin(
|
self.modeladmin = CharacterOwnershipAdmin(
|
||||||
@@ -244,6 +247,7 @@ class TestCharacterOwnershipAdmin(TestCaseWithTestData):
|
|||||||
|
|
||||||
|
|
||||||
class TestOwnershipRecordAdmin(TestCaseWithTestData):
|
class TestOwnershipRecordAdmin(TestCaseWithTestData):
|
||||||
|
fixtures = ["disable_analytics"]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.modeladmin = OwnershipRecordAdmin(
|
self.modeladmin = OwnershipRecordAdmin(
|
||||||
@@ -270,6 +274,7 @@ class TestOwnershipRecordAdmin(TestCaseWithTestData):
|
|||||||
|
|
||||||
|
|
||||||
class TestStateAdmin(TestCaseWithTestData):
|
class TestStateAdmin(TestCaseWithTestData):
|
||||||
|
fixtures = ["disable_analytics"]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.modeladmin = StateAdmin(
|
self.modeladmin = StateAdmin(
|
||||||
@@ -299,6 +304,7 @@ class TestStateAdmin(TestCaseWithTestData):
|
|||||||
|
|
||||||
|
|
||||||
class TestUserAdmin(TestCaseWithTestData):
|
class TestUserAdmin(TestCaseWithTestData):
|
||||||
|
fixtures = ["disable_analytics"]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
@@ -344,7 +350,7 @@ class TestUserAdmin(TestCaseWithTestData):
|
|||||||
self.assertEqual(user_main_organization(self.user_3), expected)
|
self.assertEqual(user_main_organization(self.user_3), expected)
|
||||||
|
|
||||||
def test_user_main_organization_u4(self):
|
def test_user_main_organization_u4(self):
|
||||||
expected="Xavier's School for Gifted Youngsters<br>The X-Men"
|
expected = "Xavier's School for Gifted Youngsters<br>The X-Men"
|
||||||
self.assertEqual(user_main_organization(self.user_4), expected)
|
self.assertEqual(user_main_organization(self.user_4), expected)
|
||||||
|
|
||||||
def test_characters_u1(self):
|
def test_characters_u1(self):
|
||||||
@@ -419,7 +425,7 @@ class TestUserAdmin(TestCaseWithTestData):
|
|||||||
|
|
||||||
# actions
|
# actions
|
||||||
|
|
||||||
@patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True)
|
@patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True, unsafe=True)
|
||||||
@patch(MODULE_PATH + '.update_character')
|
@patch(MODULE_PATH + '.update_character')
|
||||||
def test_action_update_main_character_model(
|
def test_action_update_main_character_model(
|
||||||
self, mock_task, mock_message_user
|
self, mock_task, mock_message_user
|
||||||
@@ -537,6 +543,42 @@ class TestUserAdmin(TestCaseWithTestData):
|
|||||||
self.assertEqual(response.status_code, expected)
|
self.assertEqual(response.status_code, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserAdminChangeForm(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls) -> None:
|
||||||
|
super().setUpClass()
|
||||||
|
cls.modeladmin = UserAdmin(model=User, admin_site=AdminSite())
|
||||||
|
|
||||||
|
def test_should_show_groups_available_to_user_with_blue_state_only(self):
|
||||||
|
# given
|
||||||
|
superuser = User.objects.create_superuser("Super")
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
character = AuthUtils.add_main_character_2(
|
||||||
|
user,
|
||||||
|
name="Bruce Wayne",
|
||||||
|
character_id=1001,
|
||||||
|
corp_id=2001,
|
||||||
|
corp_name="Wayne Technologies"
|
||||||
|
)
|
||||||
|
blue_state = State.objects.get(name="Blue")
|
||||||
|
blue_state.member_characters.add(character)
|
||||||
|
member_state = AuthUtils.get_member_state()
|
||||||
|
group_1 = Group.objects.create(name="Group 1")
|
||||||
|
group_2 = Group.objects.create(name="Group 2")
|
||||||
|
group_2.authgroup.states.add(blue_state)
|
||||||
|
group_3 = Group.objects.create(name="Group 3")
|
||||||
|
group_3.authgroup.states.add(member_state)
|
||||||
|
self.client.force_login(superuser)
|
||||||
|
# when
|
||||||
|
response = self.client.get(f"/admin/authentication/user/{user.pk}/change/")
|
||||||
|
# then
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
soup = BeautifulSoup(response.rendered_content, features="html.parser")
|
||||||
|
groups_select = soup.find("select", {"id": "id_groups"}).find_all('option')
|
||||||
|
group_ids = {int(option["value"]) for option in groups_select}
|
||||||
|
self.assertSetEqual(group_ids, {group_1.pk, group_2.pk})
|
||||||
|
|
||||||
|
|
||||||
class TestMakeServicesHooksActions(TestCaseWithTestData):
|
class TestMakeServicesHooksActions(TestCaseWithTestData):
|
||||||
|
|
||||||
class MyServicesHookTypeA(ServicesHook):
|
class MyServicesHookTypeA(ServicesHook):
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from math import ceil
|
from math import ceil
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import requests
|
||||||
import requests_mock
|
import requests_mock
|
||||||
from packaging.version import Version as Pep440Version
|
from packaging.version import Version as Pep440Version
|
||||||
|
|
||||||
@@ -54,7 +55,6 @@ TEST_VERSION = '2.6.5'
|
|||||||
|
|
||||||
|
|
||||||
class TestStatusOverviewTag(TestCase):
|
class TestStatusOverviewTag(TestCase):
|
||||||
|
|
||||||
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
||||||
@patch(MODULE_PATH + '.admin_status._fetch_celery_queue_length')
|
@patch(MODULE_PATH + '.admin_status._fetch_celery_queue_length')
|
||||||
@patch(MODULE_PATH + '.admin_status._current_version_summary')
|
@patch(MODULE_PATH + '.admin_status._current_version_summary')
|
||||||
@@ -65,6 +65,7 @@ class TestStatusOverviewTag(TestCase):
|
|||||||
mock_current_version_info,
|
mock_current_version_info,
|
||||||
mock_fetch_celery_queue_length
|
mock_fetch_celery_queue_length
|
||||||
):
|
):
|
||||||
|
# given
|
||||||
notifications = {
|
notifications = {
|
||||||
'notifications': GITHUB_NOTIFICATION_ISSUES[:5]
|
'notifications': GITHUB_NOTIFICATION_ISSUES[:5]
|
||||||
}
|
}
|
||||||
@@ -82,22 +83,20 @@ class TestStatusOverviewTag(TestCase):
|
|||||||
}
|
}
|
||||||
mock_current_version_info.return_value = version_info
|
mock_current_version_info.return_value = version_info
|
||||||
mock_fetch_celery_queue_length.return_value = 3
|
mock_fetch_celery_queue_length.return_value = 3
|
||||||
|
# when
|
||||||
result = status_overview()
|
result = status_overview()
|
||||||
expected = {
|
# then
|
||||||
'notifications': GITHUB_NOTIFICATION_ISSUES[:5],
|
self.assertEqual(result["notifications"], GITHUB_NOTIFICATION_ISSUES[:5])
|
||||||
'latest_major': True,
|
self.assertTrue(result["latest_major"])
|
||||||
'latest_minor': True,
|
self.assertTrue(result["latest_minor"])
|
||||||
'latest_patch': True,
|
self.assertTrue(result["latest_patch"])
|
||||||
'latest_beta': False,
|
self.assertFalse(result["latest_beta"])
|
||||||
'current_version': TEST_VERSION,
|
self.assertEqual(result["current_version"], TEST_VERSION)
|
||||||
'latest_major_version': '2.4.5',
|
self.assertEqual(result["latest_major_version"], '2.4.5')
|
||||||
'latest_minor_version': '2.4.0',
|
self.assertEqual(result["latest_minor_version"], '2.4.0')
|
||||||
'latest_patch_version': '2.4.5',
|
self.assertEqual(result["latest_patch_version"], '2.4.5')
|
||||||
'latest_beta_version': '2.4.4a1',
|
self.assertEqual(result["latest_beta_version"], '2.4.4a1')
|
||||||
'task_queue_length': 3,
|
self.assertEqual(result["task_queue_length"], 3)
|
||||||
}
|
|
||||||
self.assertEqual(result, expected)
|
|
||||||
|
|
||||||
|
|
||||||
class TestNotifications(TestCase):
|
class TestNotifications(TestCase):
|
||||||
@@ -307,3 +306,25 @@ class TestFetchListFromGitlab(TestCase):
|
|||||||
result = _fetch_list_from_gitlab(self.url, max_pages=max_pages)
|
result = _fetch_list_from_gitlab(self.url, max_pages=max_pages)
|
||||||
self.assertEqual(result, GITHUB_TAGS[:4])
|
self.assertEqual(result, GITHUB_TAGS[:4])
|
||||||
self.assertEqual(requests_mocker.call_count, max_pages)
|
self.assertEqual(requests_mocker.call_count, max_pages)
|
||||||
|
|
||||||
|
@requests_mock.mock()
|
||||||
|
@patch(MODULE_PATH + '.admin_status.logger')
|
||||||
|
def test_should_not_raise_any_exception_from_github_request_but_log_as_warning(
|
||||||
|
self, requests_mocker, mock_logger
|
||||||
|
):
|
||||||
|
for my_exception in [
|
||||||
|
requests.exceptions.ConnectionError,
|
||||||
|
requests.exceptions.HTTPError,
|
||||||
|
requests.exceptions.URLRequired,
|
||||||
|
requests.exceptions.TooManyRedirects,
|
||||||
|
requests.exceptions.ConnectTimeout,
|
||||||
|
requests.exceptions.Timeout,
|
||||||
|
|
||||||
|
]:
|
||||||
|
requests_mocker.get(self.url, exc=my_exception)
|
||||||
|
try:
|
||||||
|
result = _fetch_list_from_gitlab(self.url)
|
||||||
|
except Exception as ex:
|
||||||
|
self.fail(f"Unexpected exception raised: {ex}")
|
||||||
|
self.assertTrue(mock_logger.warning.called)
|
||||||
|
self.assertListEqual(result, [])
|
||||||
|
|||||||
@@ -193,6 +193,8 @@
|
|||||||
"columnDefs": [
|
"columnDefs": [
|
||||||
{ "sortable": false, "targets": [1] },
|
{ "sortable": false, "targets": [1] },
|
||||||
],
|
],
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0
|
||||||
});
|
});
|
||||||
$('#table-members').DataTable({
|
$('#table-members').DataTable({
|
||||||
"columnDefs": [
|
"columnDefs": [
|
||||||
@@ -200,6 +202,8 @@
|
|||||||
{ "sortable": false, "targets": [0, 2] },
|
{ "sortable": false, "targets": [0, 2] },
|
||||||
],
|
],
|
||||||
"order": [[ 1, "asc" ]],
|
"order": [[ 1, "asc" ]],
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0
|
||||||
});
|
});
|
||||||
$('#table-unregistered').DataTable({
|
$('#table-unregistered').DataTable({
|
||||||
"columnDefs": [
|
"columnDefs": [
|
||||||
@@ -207,6 +211,8 @@
|
|||||||
{ "sortable": false, "targets": [0, 2] },
|
{ "sortable": false, "targets": [0, 2] },
|
||||||
],
|
],
|
||||||
"order": [[ 1, "asc" ]],
|
"order": [[ 1, "asc" ]],
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,6 +43,9 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block extra_script %}
|
{% block extra_script %}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
$('#table-search').DataTable();
|
$('#table-search').DataTable({
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0
|
||||||
|
});
|
||||||
});
|
});
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,13 +1,27 @@
|
|||||||
from django.db import models
|
import logging
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from .managers import EveCharacterManager, EveCharacterProviderManager
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from .managers import EveCorporationManager, EveCorporationProviderManager
|
from django.db import models
|
||||||
from .managers import EveAllianceManager, EveAllianceProviderManager
|
from esi.models import Token
|
||||||
|
|
||||||
|
from allianceauth.notifications import notify
|
||||||
|
|
||||||
from . import providers
|
from . import providers
|
||||||
from .evelinks import eveimageserver
|
from .evelinks import eveimageserver
|
||||||
|
from .managers import (
|
||||||
|
EveAllianceManager,
|
||||||
|
EveAllianceProviderManager,
|
||||||
|
EveCharacterManager,
|
||||||
|
EveCharacterProviderManager,
|
||||||
|
EveCorporationManager,
|
||||||
|
EveCorporationProviderManager,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_DEFAULT_IMAGE_SIZE = 32
|
_DEFAULT_IMAGE_SIZE = 32
|
||||||
|
DOOMHEIM_CORPORATION_ID = 1000001
|
||||||
|
|
||||||
|
|
||||||
class EveFactionInfo(models.Model):
|
class EveFactionInfo(models.Model):
|
||||||
@@ -68,13 +82,12 @@ class EveAllianceInfo(models.Model):
|
|||||||
for corp_id in alliance.corp_ids:
|
for corp_id in alliance.corp_ids:
|
||||||
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
|
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
|
||||||
EveCorporationInfo.objects.create_corporation(corp_id)
|
EveCorporationInfo.objects.create_corporation(corp_id)
|
||||||
EveCorporationInfo.objects.filter(
|
EveCorporationInfo.objects.filter(corporation_id__in=alliance.corp_ids).update(
|
||||||
corporation_id__in=alliance.corp_ids).update(alliance=self
|
alliance=self
|
||||||
)
|
)
|
||||||
EveCorporationInfo.objects\
|
EveCorporationInfo.objects.filter(alliance=self).exclude(
|
||||||
.filter(alliance=self)\
|
corporation_id__in=alliance.corp_ids
|
||||||
.exclude(corporation_id__in=alliance.corp_ids)\
|
).update(alliance=None)
|
||||||
.update(alliance=None)
|
|
||||||
|
|
||||||
def update_alliance(self, alliance: providers.Alliance = None):
|
def update_alliance(self, alliance: providers.Alliance = None):
|
||||||
if alliance is None:
|
if alliance is None:
|
||||||
@@ -182,6 +195,7 @@ class EveCorporationInfo(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class EveCharacter(models.Model):
|
class EveCharacter(models.Model):
|
||||||
|
"""Character in Eve Online"""
|
||||||
character_id = models.PositiveIntegerField(unique=True)
|
character_id = models.PositiveIntegerField(unique=True)
|
||||||
character_name = models.CharField(max_length=254, unique=True)
|
character_name = models.CharField(max_length=254, unique=True)
|
||||||
corporation_id = models.PositiveIntegerField()
|
corporation_id = models.PositiveIntegerField()
|
||||||
@@ -198,12 +212,20 @@ class EveCharacter(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
indexes = [
|
indexes = [
|
||||||
models.Index(fields=['corporation_id',]),
|
models.Index(fields=['corporation_id',]),
|
||||||
models.Index(fields=['alliance_id',]),
|
models.Index(fields=['alliance_id',]),
|
||||||
models.Index(fields=['corporation_name',]),
|
models.Index(fields=['corporation_name',]),
|
||||||
models.Index(fields=['alliance_name',]),
|
models.Index(fields=['alliance_name',]),
|
||||||
models.Index(fields=['faction_id',]),
|
models.Index(fields=['faction_id',]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.character_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_biomassed(self) -> bool:
|
||||||
|
"""Whether this character is dead or not."""
|
||||||
|
return self.corporation_id == DOOMHEIM_CORPORATION_ID
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alliance(self) -> Union[EveAllianceInfo, None]:
|
def alliance(self) -> Union[EveAllianceInfo, None]:
|
||||||
@@ -249,10 +271,36 @@ class EveCharacter(models.Model):
|
|||||||
self.faction_id = character.faction.id
|
self.faction_id = character.faction.id
|
||||||
self.faction_name = character.faction.name
|
self.faction_name = character.faction.name
|
||||||
self.save()
|
self.save()
|
||||||
|
if self.is_biomassed:
|
||||||
|
self._remove_tokens_of_biomassed_character()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __str__(self):
|
def _remove_tokens_of_biomassed_character(self) -> None:
|
||||||
return self.character_name
|
"""Remove tokens of this biomassed character."""
|
||||||
|
try:
|
||||||
|
user = self.character_ownership.user
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
return
|
||||||
|
tokens_to_delete = Token.objects.filter(character_id=self.character_id)
|
||||||
|
tokens_count = tokens_to_delete.count()
|
||||||
|
if not tokens_count:
|
||||||
|
return
|
||||||
|
tokens_to_delete.delete()
|
||||||
|
logger.info(
|
||||||
|
"%d tokens from user %s for biomassed character %s [id:%s] deleted.",
|
||||||
|
tokens_count,
|
||||||
|
user,
|
||||||
|
self,
|
||||||
|
self.character_id,
|
||||||
|
)
|
||||||
|
notify(
|
||||||
|
user=user,
|
||||||
|
title=f"Character {self} biomassed",
|
||||||
|
message=(
|
||||||
|
f"Your former character {self} has been biomassed "
|
||||||
|
"and has been removed from the list of your alts."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generic_portrait_url(
|
def generic_portrait_url(
|
||||||
@@ -336,7 +384,6 @@ class EveCharacter(models.Model):
|
|||||||
"""image URL for alliance of this character or empty string"""
|
"""image URL for alliance of this character or empty string"""
|
||||||
return self.alliance_logo_url(256)
|
return self.alliance_logo_url(256)
|
||||||
|
|
||||||
|
|
||||||
def faction_logo_url(self, size=_DEFAULT_IMAGE_SIZE) -> str:
|
def faction_logo_url(self, size=_DEFAULT_IMAGE_SIZE) -> str:
|
||||||
"""image URL for alliance of this character or empty string"""
|
"""image URL for alliance of this character or empty string"""
|
||||||
if self.faction_id:
|
if self.faction_id:
|
||||||
|
|||||||
@@ -13,17 +13,18 @@ from allianceauth import __version__
|
|||||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||||
os.path.abspath(__file__)), 'swagger.json'
|
os.path.abspath(__file__)), 'swagger.json'
|
||||||
)
|
)
|
||||||
"""
|
|
||||||
Swagger spec operations:
|
|
||||||
|
|
||||||
get_alliances_alliance_id
|
# for the love of Bob please add operations you use here. I'm tired of breaking undocumented things.
|
||||||
get_alliances_alliance_id_corporations
|
ESI_OPERATIONS=[
|
||||||
get_corporations_corporation_id
|
'get_alliances_alliance_id',
|
||||||
get_characters_character_id
|
'get_alliances_alliance_id_corporations',
|
||||||
get_universe_types_type_id
|
'get_corporations_corporation_id',
|
||||||
post_character_affiliation
|
'get_characters_character_id',
|
||||||
get_universe_factions
|
'post_characters_affiliation',
|
||||||
"""
|
'get_universe_types_type_id',
|
||||||
|
'get_universe_factions',
|
||||||
|
'post_universe_names',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -169,7 +170,7 @@ class EveProvider:
|
|||||||
"""
|
"""
|
||||||
:return: an ItemType object for the given ID
|
:return: an ItemType object for the given ID
|
||||||
"""
|
"""
|
||||||
raise NotImplemented()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class EveSwaggerProvider(EveProvider):
|
class EveSwaggerProvider(EveProvider):
|
||||||
@@ -206,7 +207,8 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'esi'
|
return 'esi'
|
||||||
|
|
||||||
def get_alliance(self, alliance_id):
|
def get_alliance(self, alliance_id: int) -> Alliance:
|
||||||
|
"""Fetch alliance from ESI."""
|
||||||
try:
|
try:
|
||||||
data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result()
|
data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result()
|
||||||
corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result()
|
corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result()
|
||||||
@@ -222,7 +224,8 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
except HTTPNotFound:
|
except HTTPNotFound:
|
||||||
raise ObjectNotFound(alliance_id, 'alliance')
|
raise ObjectNotFound(alliance_id, 'alliance')
|
||||||
|
|
||||||
def get_corp(self, corp_id):
|
def get_corp(self, corp_id: int) -> Corporation:
|
||||||
|
"""Fetch corporation from ESI."""
|
||||||
try:
|
try:
|
||||||
data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result()
|
data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result()
|
||||||
model = Corporation(
|
model = Corporation(
|
||||||
@@ -238,29 +241,43 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
except HTTPNotFound:
|
except HTTPNotFound:
|
||||||
raise ObjectNotFound(corp_id, 'corporation')
|
raise ObjectNotFound(corp_id, 'corporation')
|
||||||
|
|
||||||
def get_character(self, character_id):
|
def get_character(self, character_id: int) -> Character:
|
||||||
|
"""Fetch character from ESI."""
|
||||||
try:
|
try:
|
||||||
data = self.client.Character.get_characters_character_id(character_id=character_id).result()
|
character_name = self._fetch_character_name(character_id)
|
||||||
affiliation = self.client.Character.post_characters_affiliation(characters=[character_id]).result()[0]
|
affiliation = self.client.Character.post_characters_affiliation(characters=[character_id]).result()[0]
|
||||||
|
|
||||||
model = Character(
|
model = Character(
|
||||||
id=character_id,
|
id=character_id,
|
||||||
name=data['name'],
|
name=character_name,
|
||||||
corp_id=affiliation['corporation_id'],
|
corp_id=affiliation['corporation_id'],
|
||||||
alliance_id=affiliation['alliance_id'] if 'alliance_id' in affiliation else None,
|
alliance_id=affiliation['alliance_id'] if 'alliance_id' in affiliation else None,
|
||||||
faction_id=affiliation['faction_id'] if 'faction_id' in affiliation else None,
|
faction_id=affiliation['faction_id'] if 'faction_id' in affiliation else None,
|
||||||
)
|
)
|
||||||
return model
|
return model
|
||||||
except (HTTPNotFound, HTTPUnprocessableEntity):
|
except (HTTPNotFound, HTTPUnprocessableEntity, ObjectNotFound):
|
||||||
raise ObjectNotFound(character_id, 'character')
|
raise ObjectNotFound(character_id, 'character')
|
||||||
|
|
||||||
|
def _fetch_character_name(self, character_id: int) -> str:
|
||||||
|
"""Fetch character name from ESI."""
|
||||||
|
data = self.client.Universe.post_universe_names(ids=[character_id]).result()
|
||||||
|
character = data.pop() if data else None
|
||||||
|
if (
|
||||||
|
not character
|
||||||
|
or character["category"] != "character"
|
||||||
|
or character["id"] != character_id
|
||||||
|
):
|
||||||
|
raise ObjectNotFound(character_id, 'character')
|
||||||
|
return character["name"]
|
||||||
|
|
||||||
def get_all_factions(self):
|
def get_all_factions(self):
|
||||||
|
"""Fetch all factions from ESI."""
|
||||||
if not self._faction_list:
|
if not self._faction_list:
|
||||||
self._faction_list = self.client.Universe.get_universe_factions().result()
|
self._faction_list = self.client.Universe.get_universe_factions().result()
|
||||||
return self._faction_list
|
return self._faction_list
|
||||||
|
|
||||||
def get_faction(self, faction_id):
|
def get_faction(self, faction_id: int):
|
||||||
faction_id=int(faction_id)
|
"""Fetch faction from ESI."""
|
||||||
|
faction_id = int(faction_id)
|
||||||
try:
|
try:
|
||||||
if not self._faction_list:
|
if not self._faction_list:
|
||||||
_ = self.get_all_factions()
|
_ = self.get_all_factions()
|
||||||
@@ -272,7 +289,8 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
except (HTTPNotFound, HTTPUnprocessableEntity, KeyError):
|
except (HTTPNotFound, HTTPUnprocessableEntity, KeyError):
|
||||||
raise ObjectNotFound(faction_id, 'faction')
|
raise ObjectNotFound(faction_id, 'faction')
|
||||||
|
|
||||||
def get_itemtype(self, type_id):
|
def get_itemtype(self, type_id: int) -> ItemType:
|
||||||
|
"""Fetch inventory item from ESI."""
|
||||||
try:
|
try:
|
||||||
data = self.client.Universe.get_universe_types_type_id(type_id=type_id).result()
|
data = self.client.Universe.get_universe_types_type_id(type_id=type_id).result()
|
||||||
return ItemType(id=type_id, name=data['name'])
|
return ItemType(id=type_id, name=data['name'])
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,12 +1,11 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from .models import EveAllianceInfo
|
|
||||||
from .models import EveCharacter
|
|
||||||
from .models import EveCorporationInfo
|
|
||||||
|
|
||||||
|
from .models import EveAllianceInfo, EveCharacter, EveCorporationInfo
|
||||||
from . import providers
|
from . import providers
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
TASK_PRIORITY = 7
|
TASK_PRIORITY = 7
|
||||||
@@ -32,8 +31,8 @@ def update_alliance(alliance_id):
|
|||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def update_character(character_id):
|
def update_character(character_id: int) -> None:
|
||||||
"""Update given character from ESI"""
|
"""Update given character from ESI."""
|
||||||
EveCharacter.objects.update_character(character_id)
|
EveCharacter.objects.update_character(character_id)
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +48,6 @@ def run_model_update():
|
|||||||
for alliance in EveAllianceInfo.objects.all().values('alliance_id'):
|
for alliance in EveAllianceInfo.objects.all().values('alliance_id'):
|
||||||
update_alliance.apply_async(args=[alliance['alliance_id']], priority=TASK_PRIORITY)
|
update_alliance.apply_async(args=[alliance['alliance_id']], priority=TASK_PRIORITY)
|
||||||
|
|
||||||
#update existing character models if required
|
|
||||||
# update existing character models
|
# update existing character models
|
||||||
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
|
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
|
||||||
for character_ids_chunk in chunks(character_ids, CHUNK_SIZE):
|
for character_ids_chunk in chunks(character_ids, CHUNK_SIZE):
|
||||||
@@ -66,17 +64,17 @@ def update_character_chunk(character_ids_chunk: list):
|
|||||||
.post_characters_affiliation(characters=character_ids_chunk).result()
|
.post_characters_affiliation(characters=character_ids_chunk).result()
|
||||||
character_names = providers.provider.client.Universe\
|
character_names = providers.provider.client.Universe\
|
||||||
.post_universe_names(ids=character_ids_chunk).result()
|
.post_universe_names(ids=character_ids_chunk).result()
|
||||||
except:
|
except OSError:
|
||||||
logger.error("Failed to bulk update characters. Attempting single updates")
|
logger.info("Failed to bulk update characters. Attempting single updates")
|
||||||
for character_id in character_ids_chunk:
|
for character_id in character_ids_chunk:
|
||||||
update_character.apply_async(
|
update_character.apply_async(
|
||||||
args=[character_id], priority=TASK_PRIORITY
|
args=[character_id], priority=TASK_PRIORITY
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
affiliations = {
|
affiliations = {
|
||||||
affiliation.get('character_id'): affiliation
|
affiliation.get('character_id'): affiliation
|
||||||
for affiliation in affiliations_raw
|
for affiliation in affiliations_raw
|
||||||
}
|
}
|
||||||
# add character names to affiliations
|
# add character names to affiliations
|
||||||
for character in character_names:
|
for character in character_names:
|
||||||
@@ -109,5 +107,5 @@ def update_character_chunk(character_ids_chunk: list):
|
|||||||
|
|
||||||
if corp_changed or alliance_changed or name_changed:
|
if corp_changed or alliance_changed or name_changed:
|
||||||
update_character.apply_async(
|
update_character.apply_async(
|
||||||
args=[character.get('character_id')], priority=TASK_PRIORITY
|
args=[character.get('character_id')], priority=TASK_PRIORITY
|
||||||
)
|
)
|
||||||
|
|||||||
168
allianceauth/eveonline/tests/esi_client_stub.py
Normal file
168
allianceauth/eveonline/tests/esi_client_stub.py
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
from bravado.exception import HTTPNotFound
|
||||||
|
|
||||||
|
|
||||||
|
class BravadoResponseStub:
|
||||||
|
"""Stub for IncomingResponse in bravado, e.g. for HTTPError exceptions"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, status_code, reason="", text="", headers=None, raw_bytes=None
|
||||||
|
) -> None:
|
||||||
|
self.reason = reason
|
||||||
|
self.status_code = status_code
|
||||||
|
self.text = text
|
||||||
|
self.headers = headers if headers else dict()
|
||||||
|
self.raw_bytes = raw_bytes
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.status_code} {self.reason}"
|
||||||
|
|
||||||
|
|
||||||
|
class BravadoOperationStub:
|
||||||
|
"""Stub to simulate the operation object return from bravado via django-esi"""
|
||||||
|
|
||||||
|
class RequestConfig:
|
||||||
|
def __init__(self, also_return_response):
|
||||||
|
self.also_return_response = also_return_response
|
||||||
|
|
||||||
|
class ResponseStub:
|
||||||
|
def __init__(self, headers):
|
||||||
|
self.headers = headers
|
||||||
|
|
||||||
|
def __init__(self, data, headers: dict = None, also_return_response: bool = False):
|
||||||
|
self._data = data
|
||||||
|
self._headers = headers if headers else {"x-pages": 1}
|
||||||
|
self.request_config = BravadoOperationStub.RequestConfig(also_return_response)
|
||||||
|
|
||||||
|
def result(self, **kwargs):
|
||||||
|
if self.request_config.also_return_response:
|
||||||
|
return [self._data, self.ResponseStub(self._headers)]
|
||||||
|
else:
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
def results(self, **kwargs):
|
||||||
|
return self.result(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EsiClientStub:
|
||||||
|
"""Stub for an ESI client."""
|
||||||
|
class Alliance:
|
||||||
|
@staticmethod
|
||||||
|
def get_alliances_alliance_id(alliance_id):
|
||||||
|
data = {
|
||||||
|
3001: {
|
||||||
|
"name": "Wayne Enterprises",
|
||||||
|
"ticker": "WYE",
|
||||||
|
"executor_corporation_id": 2001
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
return BravadoOperationStub(data[int(alliance_id)])
|
||||||
|
except KeyError:
|
||||||
|
response = BravadoResponseStub(
|
||||||
|
404, f"Alliance with ID {alliance_id} not found"
|
||||||
|
)
|
||||||
|
raise HTTPNotFound(response)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_alliances_alliance_id_corporations(alliance_id):
|
||||||
|
data = [2001, 2002, 2003]
|
||||||
|
return BravadoOperationStub(data)
|
||||||
|
|
||||||
|
class Character:
|
||||||
|
@staticmethod
|
||||||
|
def get_characters_character_id(character_id):
|
||||||
|
data = {
|
||||||
|
1001: {
|
||||||
|
"corporation_id": 2001,
|
||||||
|
"name": "Bruce Wayne",
|
||||||
|
},
|
||||||
|
1002: {
|
||||||
|
"corporation_id": 2001,
|
||||||
|
"name": "Peter Parker",
|
||||||
|
},
|
||||||
|
1011: {
|
||||||
|
"corporation_id": 2011,
|
||||||
|
"name": "Lex Luthor",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
return BravadoOperationStub(data[int(character_id)])
|
||||||
|
except KeyError:
|
||||||
|
response = BravadoResponseStub(
|
||||||
|
404, f"Character with ID {character_id} not found"
|
||||||
|
)
|
||||||
|
raise HTTPNotFound(response)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def post_characters_affiliation(characters: list):
|
||||||
|
data = [
|
||||||
|
{'character_id': 1001, 'corporation_id': 2001, 'alliance_id': 3001},
|
||||||
|
{'character_id': 1002, 'corporation_id': 2001, 'alliance_id': 3001},
|
||||||
|
{'character_id': 1011, 'corporation_id': 2011},
|
||||||
|
{'character_id': 1666, 'corporation_id': 1000001},
|
||||||
|
]
|
||||||
|
return BravadoOperationStub(
|
||||||
|
[x for x in data if x['character_id'] in characters]
|
||||||
|
)
|
||||||
|
|
||||||
|
class Corporation:
|
||||||
|
@staticmethod
|
||||||
|
def get_corporations_corporation_id(corporation_id):
|
||||||
|
data = {
|
||||||
|
2001: {
|
||||||
|
"ceo_id": 1091,
|
||||||
|
"member_count": 10,
|
||||||
|
"name": "Wayne Technologies",
|
||||||
|
"ticker": "WTE",
|
||||||
|
"alliance_id": 3001
|
||||||
|
},
|
||||||
|
2002: {
|
||||||
|
"ceo_id": 1092,
|
||||||
|
"member_count": 10,
|
||||||
|
"name": "Wayne Food",
|
||||||
|
"ticker": "WFO",
|
||||||
|
"alliance_id": 3001
|
||||||
|
},
|
||||||
|
2003: {
|
||||||
|
"ceo_id": 1093,
|
||||||
|
"member_count": 10,
|
||||||
|
"name": "Wayne Energy",
|
||||||
|
"ticker": "WEG",
|
||||||
|
"alliance_id": 3001
|
||||||
|
},
|
||||||
|
2011: {
|
||||||
|
"ceo_id": 1,
|
||||||
|
"member_count": 3,
|
||||||
|
"name": "LexCorp",
|
||||||
|
"ticker": "LC",
|
||||||
|
},
|
||||||
|
1000001: {
|
||||||
|
"ceo_id": 3000001,
|
||||||
|
"creator_id": 1,
|
||||||
|
"description": "The internal corporation used for characters in graveyard.",
|
||||||
|
"member_count": 6329026,
|
||||||
|
"name": "Doomheim",
|
||||||
|
"ticker": "666",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
return BravadoOperationStub(data[int(corporation_id)])
|
||||||
|
except KeyError:
|
||||||
|
response = BravadoResponseStub(
|
||||||
|
404, f"Corporation with ID {corporation_id} not found"
|
||||||
|
)
|
||||||
|
raise HTTPNotFound(response)
|
||||||
|
|
||||||
|
class Universe:
|
||||||
|
@staticmethod
|
||||||
|
def post_universe_names(ids: list):
|
||||||
|
data = [
|
||||||
|
{"category": "character", "id": 1001, "name": "Bruce Wayne"},
|
||||||
|
{"category": "character", "id": 1002, "name": "Peter Parker"},
|
||||||
|
{"category": "character", "id": 1011, "name": "Lex Luthor"},
|
||||||
|
{"category": "character", "id": 1666, "name": "Hal Jordan"},
|
||||||
|
{"category": "corporation", "id": 2001, "name": "Wayne Technologies"},
|
||||||
|
{"category": "corporation","id": 2002, "name": "Wayne Food"},
|
||||||
|
{"category": "corporation","id": 1000001, "name": "Doomheim"},
|
||||||
|
]
|
||||||
|
return BravadoOperationStub([x for x in data if x['id'] in ids])
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from esi.models import Token
|
||||||
|
|
||||||
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
|
||||||
from ..models import (
|
|
||||||
EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
|
|
||||||
)
|
|
||||||
from ..providers import Alliance, Corporation, Character
|
|
||||||
from ..evelinks import eveimageserver
|
from ..evelinks import eveimageserver
|
||||||
|
from ..models import EveAllianceInfo, EveCharacter, EveCorporationInfo, EveFactionInfo
|
||||||
|
from ..providers import Alliance, Character, Corporation
|
||||||
|
from .esi_client_stub import EsiClientStub
|
||||||
|
|
||||||
|
|
||||||
class EveCharacterTestCase(TestCase):
|
class EveCharacterTestCase(TestCase):
|
||||||
@@ -402,8 +405,8 @@ class EveAllianceTestCase(TestCase):
|
|||||||
my_alliance.save()
|
my_alliance.save()
|
||||||
my_alliance.populate_alliance()
|
my_alliance.populate_alliance()
|
||||||
|
|
||||||
for corporation in EveCorporationInfo.objects\
|
for corporation in (
|
||||||
.filter(corporation_id__in=[2001, 2002]
|
EveCorporationInfo.objects.filter(corporation_id__in=[2001, 2002])
|
||||||
):
|
):
|
||||||
self.assertEqual(corporation.alliance, my_alliance)
|
self.assertEqual(corporation.alliance, my_alliance)
|
||||||
|
|
||||||
@@ -587,3 +590,98 @@ class EveCorporationTestCase(TestCase):
|
|||||||
self.my_corp.logo_url_256,
|
self.my_corp.logo_url_256,
|
||||||
'https://images.evetech.net/corporations/2001/logo?size=256'
|
'https://images.evetech.net/corporations/2001/logo?size=256'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch('allianceauth.eveonline.providers.esi_client_factory')
|
||||||
|
@patch("allianceauth.eveonline.models.notify")
|
||||||
|
class TestCharacterUpdate(TestCase):
|
||||||
|
def test_should_update_normal_character(self, mock_notify, mock_esi_client_factory):
|
||||||
|
# given
|
||||||
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
|
my_character = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name="not my name",
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name="Wayne Food",
|
||||||
|
corporation_ticker="WYF",
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
my_character.update_character()
|
||||||
|
# then
|
||||||
|
my_character.refresh_from_db()
|
||||||
|
self.assertEqual(my_character.character_name, "Bruce Wayne")
|
||||||
|
self.assertEqual(my_character.corporation_id, 2001)
|
||||||
|
self.assertEqual(my_character.corporation_name, "Wayne Technologies")
|
||||||
|
self.assertEqual(my_character.corporation_ticker, "WTE")
|
||||||
|
self.assertEqual(my_character.alliance_id, 3001)
|
||||||
|
self.assertEqual(my_character.alliance_name, "Wayne Enterprises")
|
||||||
|
self.assertEqual(my_character.alliance_ticker, "WYE")
|
||||||
|
self.assertFalse(mock_notify.called)
|
||||||
|
|
||||||
|
def test_should_update_dead_character_with_owner(
|
||||||
|
self, mock_notify, mock_esi_client_factory
|
||||||
|
):
|
||||||
|
# given
|
||||||
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
|
character_1666 = EveCharacter.objects.create(
|
||||||
|
character_id=1666,
|
||||||
|
character_name="Hal Jordan",
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name="Wayne Food",
|
||||||
|
corporation_ticker="WYF",
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
user = AuthUtils.create_user("Bruce Wayne")
|
||||||
|
token_1666 = Token.objects.create(
|
||||||
|
user=user,
|
||||||
|
character_id=character_1666.character_id,
|
||||||
|
character_name=character_1666.character_name,
|
||||||
|
character_owner_hash="ABC123-1666",
|
||||||
|
)
|
||||||
|
character_1001 = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name="Bruce Wayne",
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name="Wayne Technologies",
|
||||||
|
corporation_ticker="WYT",
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
token_1001 = Token.objects.create(
|
||||||
|
user=user,
|
||||||
|
character_id=character_1001.character_id,
|
||||||
|
character_name=character_1001.character_name,
|
||||||
|
character_owner_hash="ABC123-1001",
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
character_1666.update_character()
|
||||||
|
# then
|
||||||
|
character_1666.refresh_from_db()
|
||||||
|
self.assertTrue(character_1666.is_biomassed)
|
||||||
|
self.assertNotIn(token_1666, user.token_set.all())
|
||||||
|
self.assertIn(token_1001, user.token_set.all())
|
||||||
|
with self.assertRaises(ObjectDoesNotExist):
|
||||||
|
self.assertTrue(character_1666.character_ownership)
|
||||||
|
user.profile.refresh_from_db()
|
||||||
|
self.assertIsNone(user.profile.main_character)
|
||||||
|
self.assertTrue(mock_notify.called)
|
||||||
|
|
||||||
|
def test_should_handle_dead_character_without_owner(
|
||||||
|
self, mock_notify, mock_esi_client_factory
|
||||||
|
):
|
||||||
|
# given
|
||||||
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
|
character_1666 = EveCharacter.objects.create(
|
||||||
|
character_id=1666,
|
||||||
|
character_name="Hal Jordan",
|
||||||
|
corporation_id=1011,
|
||||||
|
corporation_name="LexCorp",
|
||||||
|
corporation_ticker='LC',
|
||||||
|
alliance_id=None
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
character_1666.update_character()
|
||||||
|
# then
|
||||||
|
character_1666.refresh_from_db()
|
||||||
|
self.assertTrue(character_1666.is_biomassed)
|
||||||
|
self.assertFalse(mock_notify.called)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from jsonschema.exceptions import RefResolutionError
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from . import set_logger
|
from . import set_logger
|
||||||
|
from .esi_client_stub import EsiClientStub
|
||||||
from ..providers import (
|
from ..providers import (
|
||||||
ObjectNotFound,
|
ObjectNotFound,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -632,13 +633,7 @@ class TestEveSwaggerProvider(TestCase):
|
|||||||
|
|
||||||
@patch(MODULE_PATH + '.esi_client_factory')
|
@patch(MODULE_PATH + '.esi_client_factory')
|
||||||
def test_get_character(self, mock_esi_client_factory):
|
def test_get_character(self, mock_esi_client_factory):
|
||||||
mock_esi_client_factory.return_value \
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
.Character.get_characters_character_id \
|
|
||||||
= TestEveSwaggerProvider.esi_get_characters_character_id
|
|
||||||
mock_esi_client_factory.return_value \
|
|
||||||
.Character.post_characters_affiliation \
|
|
||||||
= TestEveSwaggerProvider.esi_post_characters_affiliation
|
|
||||||
|
|
||||||
my_provider = EveSwaggerProvider()
|
my_provider = EveSwaggerProvider()
|
||||||
|
|
||||||
# character with alliance
|
# character with alliance
|
||||||
@@ -649,8 +644,8 @@ class TestEveSwaggerProvider(TestCase):
|
|||||||
self.assertEqual(my_character.alliance_id, 3001)
|
self.assertEqual(my_character.alliance_id, 3001)
|
||||||
|
|
||||||
# character wo/ alliance
|
# character wo/ alliance
|
||||||
my_character = my_provider.get_character(1002)
|
my_character = my_provider.get_character(1011)
|
||||||
self.assertEqual(my_character.id, 1002)
|
self.assertEqual(my_character.id, 1011)
|
||||||
self.assertEqual(my_character.alliance_id, None)
|
self.assertEqual(my_character.alliance_id, None)
|
||||||
|
|
||||||
# character not found
|
# character not found
|
||||||
|
|||||||
@@ -1,245 +1,271 @@
|
|||||||
from unittest.mock import patch, Mock
|
from unittest.mock import patch
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase, TransactionTestCase, override_settings
|
||||||
|
|
||||||
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
from ..models import EveAllianceInfo, EveCharacter, EveCorporationInfo
|
||||||
from ..tasks import (
|
from ..tasks import (
|
||||||
|
run_model_update,
|
||||||
update_alliance,
|
update_alliance,
|
||||||
update_corp,
|
|
||||||
update_character,
|
update_character,
|
||||||
run_model_update
|
update_character_chunk,
|
||||||
|
update_corp,
|
||||||
)
|
)
|
||||||
|
from .esi_client_stub import EsiClientStub
|
||||||
|
|
||||||
|
|
||||||
class TestTasks(TestCase):
|
@patch('allianceauth.eveonline.providers.esi_client_factory')
|
||||||
|
class TestUpdateTasks(TestCase):
|
||||||
@patch('allianceauth.eveonline.tasks.EveCorporationInfo')
|
def test_should_update_alliance(self, mock_esi_client_factory):
|
||||||
def test_update_corp(self, mock_EveCorporationInfo):
|
# given
|
||||||
update_corp(42)
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
self.assertEqual(
|
my_alliance = EveAllianceInfo.objects.create(
|
||||||
mock_EveCorporationInfo.objects.update_corporation.call_count, 1
|
alliance_id=3001,
|
||||||
)
|
alliance_name="Wayne Enterprises",
|
||||||
self.assertEqual(
|
alliance_ticker="WYE",
|
||||||
mock_EveCorporationInfo.objects.update_corporation.call_args[0][0], 42
|
executor_corp_id=2003
|
||||||
)
|
)
|
||||||
|
# when
|
||||||
|
update_alliance(my_alliance.alliance_id)
|
||||||
|
# then
|
||||||
|
my_alliance.refresh_from_db()
|
||||||
|
self.assertEqual(my_alliance.executor_corp_id, 2001)
|
||||||
|
|
||||||
@patch('allianceauth.eveonline.tasks.EveAllianceInfo')
|
def test_should_update_character(self, mock_esi_client_factory):
|
||||||
def test_update_alliance(self, mock_EveAllianceInfo):
|
# given
|
||||||
update_alliance(42)
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
self.assertEqual(
|
my_character = EveCharacter.objects.create(
|
||||||
mock_EveAllianceInfo.objects.update_alliance.call_args[0][0], 42
|
character_id=1001,
|
||||||
)
|
character_name="Bruce Wayne",
|
||||||
self.assertEqual(
|
corporation_id=2002,
|
||||||
mock_EveAllianceInfo.objects
|
corporation_name="Wayne Food",
|
||||||
.update_alliance.return_value.populate_alliance.call_count, 1
|
corporation_ticker="WYF",
|
||||||
|
alliance_id=None
|
||||||
)
|
)
|
||||||
|
# when
|
||||||
|
update_character(my_character.character_id)
|
||||||
|
# then
|
||||||
|
my_character.refresh_from_db()
|
||||||
|
self.assertEqual(my_character.corporation_id, 2001)
|
||||||
|
|
||||||
@patch('allianceauth.eveonline.tasks.EveCharacter')
|
def test_should_update_corp(self, mock_esi_client_factory):
|
||||||
def test_update_character(self, mock_EveCharacter):
|
# given
|
||||||
update_character(42)
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
self.assertEqual(
|
EveAllianceInfo.objects.create(
|
||||||
mock_EveCharacter.objects.update_character.call_count, 1
|
alliance_id=3001,
|
||||||
|
alliance_name="Wayne Enterprises",
|
||||||
|
alliance_ticker="WYE",
|
||||||
|
executor_corp_id=2003
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
my_corporation = EveCorporationInfo.objects.create(
|
||||||
mock_EveCharacter.objects.update_character.call_args[0][0], 42
|
corporation_id=2003,
|
||||||
|
corporation_name="Wayne Food",
|
||||||
|
corporation_ticker="WFO",
|
||||||
|
member_count=1,
|
||||||
|
alliance=None,
|
||||||
|
ceo_id=1999
|
||||||
)
|
)
|
||||||
|
# when
|
||||||
|
update_corp(my_corporation.corporation_id)
|
||||||
|
# then
|
||||||
|
my_corporation.refresh_from_db()
|
||||||
|
self.assertEqual(my_corporation.alliance.alliance_id, 3001)
|
||||||
|
|
||||||
|
# @patch('allianceauth.eveonline.tasks.EveCharacter')
|
||||||
|
# def test_update_character(self, mock_EveCharacter):
|
||||||
|
# update_character(42)
|
||||||
|
# self.assertEqual(
|
||||||
|
# mock_EveCharacter.objects.update_character.call_count, 1
|
||||||
|
# )
|
||||||
|
# self.assertEqual(
|
||||||
|
# mock_EveCharacter.objects.update_character.call_args[0][0], 42
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
@patch('allianceauth.eveonline.tasks.update_character')
|
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||||
@patch('allianceauth.eveonline.tasks.update_alliance')
|
@patch('allianceauth.eveonline.providers.esi_client_factory')
|
||||||
@patch('allianceauth.eveonline.tasks.update_corp')
|
@patch('allianceauth.eveonline.tasks.providers')
|
||||||
@patch('allianceauth.eveonline.providers.provider')
|
|
||||||
@patch('allianceauth.eveonline.tasks.CHUNK_SIZE', 2)
|
@patch('allianceauth.eveonline.tasks.CHUNK_SIZE', 2)
|
||||||
class TestRunModelUpdate(TestCase):
|
class TestRunModelUpdate(TransactionTestCase):
|
||||||
|
def test_should_run_updates(self, mock_providers, mock_esi_client_factory):
|
||||||
@classmethod
|
# given
|
||||||
def setUpClass(cls):
|
mock_providers.provider.client = EsiClientStub()
|
||||||
super().setUpClass()
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
EveCorporationInfo.objects.all().delete()
|
|
||||||
EveAllianceInfo.objects.all().delete()
|
|
||||||
EveCharacter.objects.all().delete()
|
|
||||||
|
|
||||||
EveCorporationInfo.objects.create(
|
EveCorporationInfo.objects.create(
|
||||||
corporation_id=2345,
|
corporation_id=2001,
|
||||||
corporation_name='corp.name',
|
corporation_name="Wayne Technologies",
|
||||||
corporation_ticker='c.c.t',
|
corporation_ticker="WTE",
|
||||||
member_count=10,
|
member_count=10,
|
||||||
alliance=None,
|
alliance=None,
|
||||||
)
|
)
|
||||||
EveAllianceInfo.objects.create(
|
alliance_3001 = EveAllianceInfo.objects.create(
|
||||||
alliance_id=3456,
|
alliance_id=3001,
|
||||||
alliance_name='alliance.name',
|
alliance_name="Wayne Enterprises",
|
||||||
alliance_ticker='a.t',
|
alliance_ticker="WYE",
|
||||||
executor_corp_id=5,
|
executor_corp_id=2003
|
||||||
)
|
)
|
||||||
EveCharacter.objects.create(
|
corporation_2003 = EveCorporationInfo.objects.create(
|
||||||
character_id=1,
|
corporation_id=2003,
|
||||||
character_name='character.name1',
|
corporation_name="Wayne Energy",
|
||||||
corporation_id=2345,
|
corporation_ticker="WEG",
|
||||||
corporation_name='character.corp.name',
|
member_count=99,
|
||||||
corporation_ticker='c.c.t', # max 5 chars
|
alliance=None,
|
||||||
|
)
|
||||||
|
character_1001 = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name="Bruce Wayne",
|
||||||
|
corporation_id=2002,
|
||||||
|
corporation_name="Wayne Food",
|
||||||
|
corporation_ticker="WYF",
|
||||||
alliance_id=None
|
alliance_id=None
|
||||||
)
|
)
|
||||||
EveCharacter.objects.create(
|
# when
|
||||||
character_id=2,
|
|
||||||
character_name='character.name2',
|
|
||||||
corporation_id=9876,
|
|
||||||
corporation_name='character.corp.name',
|
|
||||||
corporation_ticker='c.c.t', # max 5 chars
|
|
||||||
alliance_id=3456,
|
|
||||||
alliance_name='character.alliance.name',
|
|
||||||
)
|
|
||||||
EveCharacter.objects.create(
|
|
||||||
character_id=3,
|
|
||||||
character_name='character.name3',
|
|
||||||
corporation_id=9876,
|
|
||||||
corporation_name='character.corp.name',
|
|
||||||
corporation_ticker='c.c.t', # max 5 chars
|
|
||||||
alliance_id=3456,
|
|
||||||
alliance_name='character.alliance.name',
|
|
||||||
)
|
|
||||||
EveCharacter.objects.create(
|
|
||||||
character_id=4,
|
|
||||||
character_name='character.name4',
|
|
||||||
corporation_id=9876,
|
|
||||||
corporation_name='character.corp.name',
|
|
||||||
corporation_ticker='c.c.t', # max 5 chars
|
|
||||||
alliance_id=3456,
|
|
||||||
alliance_name='character.alliance.name',
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
EveCharacter.objects.create(
|
|
||||||
character_id=5,
|
|
||||||
character_name='character.name5',
|
|
||||||
corporation_id=9876,
|
|
||||||
corporation_name='character.corp.name',
|
|
||||||
corporation_ticker='c.c.t', # max 5 chars
|
|
||||||
alliance_id=3456,
|
|
||||||
alliance_name='character.alliance.name',
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.affiliations = [
|
|
||||||
{'character_id': 1, 'corporation_id': 5},
|
|
||||||
{'character_id': 2, 'corporation_id': 9876, 'alliance_id': 3456},
|
|
||||||
{'character_id': 3, 'corporation_id': 9876, 'alliance_id': 7456},
|
|
||||||
{'character_id': 4, 'corporation_id': 9876, 'alliance_id': 3456}
|
|
||||||
]
|
|
||||||
self.names = [
|
|
||||||
{'id': 1, 'name': 'character.name1'},
|
|
||||||
{'id': 2, 'name': 'character.name2'},
|
|
||||||
{'id': 3, 'name': 'character.name3'},
|
|
||||||
{'id': 4, 'name': 'character.name4_new'}
|
|
||||||
]
|
|
||||||
|
|
||||||
def test_normal_run(
|
|
||||||
self,
|
|
||||||
mock_provider,
|
|
||||||
mock_update_corp,
|
|
||||||
mock_update_alliance,
|
|
||||||
mock_update_character,
|
|
||||||
):
|
|
||||||
def get_affiliations(characters: list):
|
|
||||||
response = [x for x in self.affiliations if x['character_id'] in characters]
|
|
||||||
mock_operator = Mock(**{'result.return_value': response})
|
|
||||||
return mock_operator
|
|
||||||
|
|
||||||
def get_names(ids: list):
|
|
||||||
response = [x for x in self.names if x['id'] in ids]
|
|
||||||
mock_operator = Mock(**{'result.return_value': response})
|
|
||||||
return mock_operator
|
|
||||||
|
|
||||||
mock_provider.client.Character.post_characters_affiliation.side_effect \
|
|
||||||
= get_affiliations
|
|
||||||
|
|
||||||
mock_provider.client.Universe.post_universe_names.side_effect = get_names
|
|
||||||
|
|
||||||
run_model_update()
|
run_model_update()
|
||||||
|
# then
|
||||||
|
character_1001.refresh_from_db()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
mock_provider.client.Character.post_characters_affiliation.call_count, 2
|
character_1001.corporation_id, 2001 # char has new corp
|
||||||
)
|
)
|
||||||
|
corporation_2003.refresh_from_db()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
mock_provider.client.Universe.post_universe_names.call_count, 2
|
corporation_2003.alliance.alliance_id, 3001 # corp has new alliance
|
||||||
|
)
|
||||||
|
alliance_3001.refresh_from_db()
|
||||||
|
self.assertEqual(
|
||||||
|
alliance_3001.executor_corp_id, 2001 # alliance has been updated
|
||||||
)
|
)
|
||||||
|
|
||||||
# character 1 has changed corp
|
|
||||||
# character 2 no change
|
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||||
# character 3 has changed alliance
|
@patch('allianceauth.eveonline.tasks.update_character', wraps=update_character)
|
||||||
# character 4 has changed name
|
@patch('allianceauth.eveonline.providers.esi_client_factory')
|
||||||
self.assertEqual(mock_update_corp.apply_async.call_count, 1)
|
@patch('allianceauth.eveonline.tasks.providers')
|
||||||
self.assertEqual(
|
@patch('allianceauth.eveonline.tasks.CHUNK_SIZE', 2)
|
||||||
int(mock_update_corp.apply_async.call_args[1]['args'][0]), 2345
|
class TestUpdateCharacterChunk(TestCase):
|
||||||
)
|
@staticmethod
|
||||||
self.assertEqual(mock_update_alliance.apply_async.call_count, 1)
|
def _updated_character_ids(spy_update_character) -> set:
|
||||||
self.assertEqual(
|
"""Character IDs passed to update_character task for update."""
|
||||||
int(mock_update_alliance.apply_async.call_args[1]['args'][0]), 3456
|
return {
|
||||||
)
|
x[1]["args"][0] for x in spy_update_character.apply_async.call_args_list
|
||||||
characters_updated = {
|
|
||||||
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
|
|
||||||
}
|
}
|
||||||
excepted = {1, 3, 4}
|
|
||||||
self.assertSetEqual(characters_updated, excepted)
|
|
||||||
|
|
||||||
def test_ignore_character_not_in_affiliations(
|
def test_should_update_corp_change(
|
||||||
self,
|
self, mock_providers, mock_esi_client_factory, spy_update_character
|
||||||
mock_provider,
|
|
||||||
mock_update_corp,
|
|
||||||
mock_update_alliance,
|
|
||||||
mock_update_character,
|
|
||||||
):
|
):
|
||||||
def get_affiliations(characters: list):
|
# given
|
||||||
response = [x for x in self.affiliations if x['character_id'] in characters]
|
mock_providers.provider.client = EsiClientStub()
|
||||||
mock_operator = Mock(**{'result.return_value': response})
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
return mock_operator
|
character_1001 = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name="Bruce Wayne",
|
||||||
|
corporation_id=2003,
|
||||||
|
corporation_name="Wayne Energy",
|
||||||
|
corporation_ticker="WEG",
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name="Wayne Enterprises",
|
||||||
|
alliance_ticker="WYE",
|
||||||
|
)
|
||||||
|
character_1002 = EveCharacter.objects.create(
|
||||||
|
character_id=1002,
|
||||||
|
character_name="Peter Parker",
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name="Wayne Technologies",
|
||||||
|
corporation_ticker="WTE",
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name="Wayne Enterprises",
|
||||||
|
alliance_ticker="WYE",
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
update_character_chunk([
|
||||||
|
character_1001.character_id, character_1002.character_id
|
||||||
|
])
|
||||||
|
# then
|
||||||
|
character_1001.refresh_from_db()
|
||||||
|
self.assertEqual(character_1001.corporation_id, 2001)
|
||||||
|
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})
|
||||||
|
|
||||||
def get_names(ids: list):
|
def test_should_update_name_change(
|
||||||
response = [x for x in self.names if x['id'] in ids]
|
self, mock_providers, mock_esi_client_factory, spy_update_character
|
||||||
mock_operator = Mock(**{'result.return_value': response})
|
|
||||||
return mock_operator
|
|
||||||
|
|
||||||
del self.affiliations[0]
|
|
||||||
|
|
||||||
mock_provider.client.Character.post_characters_affiliation.side_effect \
|
|
||||||
= get_affiliations
|
|
||||||
|
|
||||||
mock_provider.client.Universe.post_universe_names.side_effect = get_names
|
|
||||||
|
|
||||||
run_model_update()
|
|
||||||
characters_updated = {
|
|
||||||
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
|
|
||||||
}
|
|
||||||
excepted = {3, 4}
|
|
||||||
self.assertSetEqual(characters_updated, excepted)
|
|
||||||
|
|
||||||
def test_ignore_character_not_in_names(
|
|
||||||
self,
|
|
||||||
mock_provider,
|
|
||||||
mock_update_corp,
|
|
||||||
mock_update_alliance,
|
|
||||||
mock_update_character,
|
|
||||||
):
|
):
|
||||||
def get_affiliations(characters: list):
|
# given
|
||||||
response = [x for x in self.affiliations if x['character_id'] in characters]
|
mock_providers.provider.client = EsiClientStub()
|
||||||
mock_operator = Mock(**{'result.return_value': response})
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
return mock_operator
|
character_1001 = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name="Batman",
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name="Wayne Technologies",
|
||||||
|
corporation_ticker="WTE",
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name="Wayne Technologies",
|
||||||
|
alliance_ticker="WYT",
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
update_character_chunk([character_1001.character_id])
|
||||||
|
# then
|
||||||
|
character_1001.refresh_from_db()
|
||||||
|
self.assertEqual(character_1001.character_name, "Bruce Wayne")
|
||||||
|
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})
|
||||||
|
|
||||||
def get_names(ids: list):
|
def test_should_update_alliance_change(
|
||||||
response = [x for x in self.names if x['id'] in ids]
|
self, mock_providers, mock_esi_client_factory, spy_update_character
|
||||||
mock_operator = Mock(**{'result.return_value': response})
|
):
|
||||||
return mock_operator
|
# given
|
||||||
|
mock_providers.provider.client = EsiClientStub()
|
||||||
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
|
character_1001 = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name="Bruce Wayne",
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name="Wayne Technologies",
|
||||||
|
corporation_ticker="WTE",
|
||||||
|
alliance_id=None,
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
update_character_chunk([character_1001.character_id])
|
||||||
|
# then
|
||||||
|
character_1001.refresh_from_db()
|
||||||
|
self.assertEqual(character_1001.alliance_id, 3001)
|
||||||
|
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})
|
||||||
|
|
||||||
del self.names[3]
|
def test_should_not_update_when_not_changed(
|
||||||
|
self, mock_providers, mock_esi_client_factory, spy_update_character
|
||||||
|
):
|
||||||
|
# given
|
||||||
|
mock_providers.provider.client = EsiClientStub()
|
||||||
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
|
character_1001 = EveCharacter.objects.create(
|
||||||
|
character_id=1001,
|
||||||
|
character_name="Bruce Wayne",
|
||||||
|
corporation_id=2001,
|
||||||
|
corporation_name="Wayne Technologies",
|
||||||
|
corporation_ticker="WTE",
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name="Wayne Technologies",
|
||||||
|
alliance_ticker="WYT",
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
update_character_chunk([character_1001.character_id])
|
||||||
|
# then
|
||||||
|
self.assertSetEqual(self._updated_character_ids(spy_update_character), set())
|
||||||
|
|
||||||
mock_provider.client.Character.post_characters_affiliation.side_effect \
|
def test_should_fall_back_to_single_updates_when_bulk_update_failed(
|
||||||
= get_affiliations
|
self, mock_providers, mock_esi_client_factory, spy_update_character
|
||||||
|
):
|
||||||
mock_provider.client.Universe.post_universe_names.side_effect = get_names
|
# given
|
||||||
|
mock_providers.provider.client.Character.post_characters_affiliation\
|
||||||
run_model_update()
|
.side_effect = OSError
|
||||||
characters_updated = {
|
mock_esi_client_factory.return_value = EsiClientStub()
|
||||||
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
|
character_1001 = EveCharacter.objects.create(
|
||||||
}
|
character_id=1001,
|
||||||
excepted = {1, 3}
|
character_name="Bruce Wayne",
|
||||||
self.assertSetEqual(characters_updated, excepted)
|
corporation_id=2001,
|
||||||
|
corporation_name="Wayne Technologies",
|
||||||
|
corporation_ticker="WTE",
|
||||||
|
alliance_id=3001,
|
||||||
|
alliance_name="Wayne Technologies",
|
||||||
|
alliance_ticker="WYT",
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
update_character_chunk([character_1001.character_id])
|
||||||
|
# then
|
||||||
|
self.assertSetEqual(self._updated_character_ids(spy_update_character), {1001})
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
|
from django import forms
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.models import Group as BaseGroup, User
|
from django.contrib.auth.models import Group as BaseGroup, User
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
||||||
post_delete, m2m_changed
|
post_delete, m2m_changed
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .models import AuthGroup
|
from .models import AuthGroup, ReservedGroupName
|
||||||
from .models import GroupRequest
|
from .models import GroupRequest
|
||||||
|
|
||||||
if 'eve_autogroups' in apps.app_configs:
|
if 'eve_autogroups' in apps.app_configs:
|
||||||
@@ -70,8 +74,7 @@ if _has_auto_groups:
|
|||||||
managedalliancegroup__isnull=True,
|
managedalliancegroup__isnull=True,
|
||||||
managedcorpgroup__isnull=True
|
managedcorpgroup__isnull=True
|
||||||
)
|
)
|
||||||
else:
|
return queryset
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class HasLeaderFilter(admin.SimpleListFilter):
|
class HasLeaderFilter(admin.SimpleListFilter):
|
||||||
@@ -90,11 +93,22 @@ class HasLeaderFilter(admin.SimpleListFilter):
|
|||||||
return queryset.filter(authgroup__group_leaders__isnull=False)
|
return queryset.filter(authgroup__group_leaders__isnull=False)
|
||||||
elif value == 'no':
|
elif value == 'no':
|
||||||
return queryset.filter(authgroup__group_leaders__isnull=True)
|
return queryset.filter(authgroup__group_leaders__isnull=True)
|
||||||
else:
|
return queryset
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
class GroupAdminForm(forms.ModelForm):
|
||||||
|
def clean_name(self):
|
||||||
|
my_name = self.cleaned_data['name']
|
||||||
|
if ReservedGroupName.objects.filter(name__iexact=my_name).exists():
|
||||||
|
raise ValidationError(
|
||||||
|
_("This name has been reserved and can not be used for groups."),
|
||||||
|
code='reserved_name'
|
||||||
|
)
|
||||||
|
return my_name
|
||||||
|
|
||||||
|
|
||||||
class GroupAdmin(admin.ModelAdmin):
|
class GroupAdmin(admin.ModelAdmin):
|
||||||
|
form = GroupAdminForm
|
||||||
list_select_related = ('authgroup',)
|
list_select_related = ('authgroup',)
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
list_display = (
|
list_display = (
|
||||||
@@ -209,6 +223,41 @@ class GroupRequestAdmin(admin.ModelAdmin):
|
|||||||
_leave_request.boolean = True
|
_leave_request.boolean = True
|
||||||
|
|
||||||
|
|
||||||
|
class ReservedGroupNameAdminForm(forms.ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['created_by'].initial = self.current_user.username
|
||||||
|
self.fields['created_at'].initial = _("(auto)")
|
||||||
|
|
||||||
|
created_by = forms.CharField(disabled=True)
|
||||||
|
created_at = forms.CharField(disabled=True)
|
||||||
|
|
||||||
|
def clean_name(self):
|
||||||
|
my_name = self.cleaned_data['name'].lower()
|
||||||
|
if Group.objects.filter(name__iexact=my_name).exists():
|
||||||
|
raise ValidationError(
|
||||||
|
_("There already exists a group with that name."), code='already_exists'
|
||||||
|
)
|
||||||
|
return my_name
|
||||||
|
|
||||||
|
def clean_created_at(self):
|
||||||
|
return now()
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ReservedGroupName)
|
||||||
|
class ReservedGroupNameAdmin(admin.ModelAdmin):
|
||||||
|
form = ReservedGroupNameAdminForm
|
||||||
|
list_display = ("name", "created_by", "created_at")
|
||||||
|
|
||||||
|
def get_form(self, request, *args, **kwargs):
|
||||||
|
form = super().get_form(request, *args, **kwargs)
|
||||||
|
form.current_user = request.user
|
||||||
|
return form
|
||||||
|
|
||||||
|
def has_change_permission(self, *args, **kwargs) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_save, sender=Group)
|
@receiver(pre_save, sender=Group)
|
||||||
def redirect_pre_save(sender, signal=None, *args, **kwargs):
|
def redirect_pre_save(sender, signal=None, *args, **kwargs):
|
||||||
pre_save.send(BaseGroup, *args, **kwargs)
|
pre_save.send(BaseGroup, *args, **kwargs)
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ class GroupManager:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_joinable_groups_for_user(
|
def get_joinable_groups_for_user(
|
||||||
cls, user: User, include_hidden = True
|
cls, user: User, include_hidden=True
|
||||||
) -> QuerySet:
|
) -> QuerySet[Group]:
|
||||||
"""get groups a user could join incl. groups already joined"""
|
"""get groups a user could join incl. groups already joined"""
|
||||||
groups_qs = cls.get_joinable_groups(user.profile.state)
|
groups_qs = cls.get_joinable_groups(user.profile.state)
|
||||||
|
|
||||||
@@ -28,24 +28,27 @@ class GroupManager:
|
|||||||
return groups_qs
|
return groups_qs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_joinable_groups(state: State) -> QuerySet:
|
def get_joinable_groups(state: State) -> QuerySet[Group]:
|
||||||
"""get groups that can be joined by user with given state"""
|
"""get groups that can be joined by user with given state"""
|
||||||
return Group.objects\
|
return (
|
||||||
.select_related('authgroup')\
|
Group.objects
|
||||||
.exclude(authgroup__internal=True)\
|
|
||||||
.filter(Q(authgroup__states=state) | Q(authgroup__states=None))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_all_non_internal_groups() -> QuerySet:
|
|
||||||
"""get groups that are not internal"""
|
|
||||||
return Group.objects\
|
|
||||||
.select_related('authgroup')\
|
|
||||||
.exclude(authgroup__internal=True)
|
.exclude(authgroup__internal=True)
|
||||||
|
.filter(Q(authgroup__states=state) | Q(authgroup__states=None))
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_group_leaders_groups(user: User):
|
def get_all_non_internal_groups() -> QuerySet[Group]:
|
||||||
return Group.objects.select_related('authgroup').filter(authgroup__group_leaders__in=[user]) | \
|
"""get groups that are not internal"""
|
||||||
Group.objects.select_related('authgroup').filter(authgroup__group_leader_groups__in=user.groups.all())
|
return Group.objects.exclude(authgroup__internal=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_group_leaders_groups(user: User) -> QuerySet[Group]:
|
||||||
|
return (
|
||||||
|
Group.objects.filter(authgroup__group_leaders=user)
|
||||||
|
| Group.objects.filter(
|
||||||
|
authgroup__group_leader_groups__in=list(user.groups.all())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def joinable_group(group: Group, state: State) -> bool:
|
def joinable_group(group: Group, state: State) -> bool:
|
||||||
@@ -57,12 +60,12 @@ class GroupManager:
|
|||||||
:param state: allianceauth.authentication.State object
|
:param state: allianceauth.authentication.State object
|
||||||
:return: bool True if its joinable, False otherwise
|
:return: bool True if its joinable, False otherwise
|
||||||
"""
|
"""
|
||||||
if (len(group.authgroup.states.all()) != 0
|
if (
|
||||||
|
len(group.authgroup.states.all()) != 0
|
||||||
and state not in group.authgroup.states.all()
|
and state not in group.authgroup.states.all()
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
else:
|
return not group.authgroup.internal
|
||||||
return not group.authgroup.internal
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_internal_group(group: Group) -> bool:
|
def check_internal_group(group: Group) -> bool:
|
||||||
@@ -78,7 +81,7 @@ class GroupManager:
|
|||||||
return user.has_perm('auth.group_management')
|
return user.has_perm('auth.group_management')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_manage_groups(cls, user:User ) -> bool:
|
def can_manage_groups(cls, user:User) -> bool:
|
||||||
"""
|
"""
|
||||||
For use with user_passes_test decorator.
|
For use with user_passes_test decorator.
|
||||||
Check if the user can manage groups. Either has the
|
Check if the user can manage groups. Either has the
|
||||||
@@ -88,7 +91,10 @@ class GroupManager:
|
|||||||
:return: bool True if user can manage groups, False otherwise
|
:return: bool True if user can manage groups, False otherwise
|
||||||
"""
|
"""
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
return cls.has_management_permission(user) or cls.get_group_leaders_groups(user)
|
return (
|
||||||
|
cls.has_management_permission(user)
|
||||||
|
or cls.get_group_leaders_groups(user)
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -100,19 +106,19 @@ class GroupManager:
|
|||||||
:return: True if the user can manage the group
|
:return: True if the user can manage the group
|
||||||
"""
|
"""
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
return cls.has_management_permission(user) or cls.get_group_leaders_groups(user).filter(pk=group.pk).exists()
|
return (
|
||||||
|
cls.has_management_permission(user)
|
||||||
|
or cls.get_group_leaders_groups(user).filter(pk=group.pk).exists()
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def pending_requests_count_for_user(cls, user: User) -> int:
|
def pending_requests_count_for_user(cls, user: User) -> int:
|
||||||
"""Returns the number of pending group requests for the given user"""
|
"""Returns the number of pending group requests for the given user"""
|
||||||
|
|
||||||
if cls.has_management_permission(user):
|
if cls.has_management_permission(user):
|
||||||
return GroupRequest.objects.all().count()
|
return GroupRequest.objects.all().count()
|
||||||
else:
|
return (
|
||||||
return (
|
GroupRequest.objects
|
||||||
GroupRequest.objects
|
.filter(group__in=list(cls.get_group_leaders_groups(user)))
|
||||||
.filter(group__authgroup__group_leaders__exact=user)
|
.count()
|
||||||
.select_related("group__authgroup__group_leaders")
|
)
|
||||||
.count()
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Generated by Django 3.2.9 on 2021-11-11 15:56
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
('authentication', '0019_merge_20211026_0919'),
|
||||||
|
('groupmanagement', '0016_remove_grouprequest_status_field'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authgroup',
|
||||||
|
name='group_leader_groups',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Members of leader groups can process requests for this group. Use the <code>auth.group_management</code> permission to allow a user to manage all groups.<br>', related_name='leads_group_groups', to='auth.Group'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authgroup',
|
||||||
|
name='group_leaders',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Group leaders can process requests for this group. Use the <code>auth.group_management</code> permission to allow a user to manage all groups.<br>', related_name='leads_groups', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authgroup',
|
||||||
|
name='open',
|
||||||
|
field=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.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authgroup',
|
||||||
|
name='public',
|
||||||
|
field=models.BooleanField(default=False, help_text='Group is public. Any registered user is able to join this group, with visibility based on the other options set for this group.<br>Auth will not remove users from this group automatically when they are no longer authenticated.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authgroup',
|
||||||
|
name='states',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='States listed here will have the ability to join this group provided they have the proper permissions.<br>', related_name='valid_states', to='authentication.State'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 3.2.9 on 2021-11-25 18:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('groupmanagement', '0017_improve_groups_documentation'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ReservedGroupName',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Name that can not be used for groups.', max_length=150, unique=True, verbose_name='name')),
|
||||||
|
('reason', models.TextField(help_text='Reason why this name is reserved.', verbose_name='reason')),
|
||||||
|
('created_by', models.CharField(help_text='Name of the user who created this entry.', max_length=255, verbose_name='created by')),
|
||||||
|
('created_at', models.DateTimeField(default=django.utils.timezone.now, help_text='Date when this entry was created', verbose_name='created at')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
|
from typing import Set
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_save
|
from django.utils.timezone import now
|
||||||
from django.dispatch import receiver
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from allianceauth.authentication.models import State
|
from allianceauth.authentication.models import State
|
||||||
|
from allianceauth.notifications import notify
|
||||||
|
|
||||||
|
|
||||||
class GroupRequest(models.Model):
|
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=0)
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.user.username + ":" + self.group.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def main_char(self):
|
def main_char(self):
|
||||||
"""
|
"""
|
||||||
@@ -19,11 +28,22 @@ class GroupRequest(models.Model):
|
|||||||
"""
|
"""
|
||||||
return self.user.profile.main_character
|
return self.user.profile.main_character
|
||||||
|
|
||||||
def __str__(self):
|
def notify_leaders(self) -> None:
|
||||||
return self.user.username + ":" + self.group.name
|
"""Send notification to all group leaders about this request.
|
||||||
|
|
||||||
|
Note: No translations, because language for each leader is unknown
|
||||||
|
"""
|
||||||
|
if not getattr(settings, 'GROUPMANAGEMENT_REQUESTS_NOTIFICATION', False):
|
||||||
|
return
|
||||||
|
keyword = "leave" if self.leave_request else "join"
|
||||||
|
title = f"Group Management: {keyword.title()} request for {self.group.name}"
|
||||||
|
message = f"{self.user} want's to {keyword} {self.group.name}."
|
||||||
|
for appover in self.group.authgroup.group_request_approvers():
|
||||||
|
notify(user=appover, title=title, message=message, level="info")
|
||||||
|
|
||||||
|
|
||||||
class RequestLog(models.Model):
|
class RequestLog(models.Model):
|
||||||
|
"""Log entry about who joined and left a group and who approved it."""
|
||||||
request_type = models.BooleanField(null=True)
|
request_type = models.BooleanField(null=True)
|
||||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
request_info = models.CharField(max_length=254)
|
request_info = models.CharField(max_length=254)
|
||||||
@@ -61,11 +81,12 @@ class AuthGroup(models.Model):
|
|||||||
e.g. group.authgroup.internal
|
e.g. group.authgroup.internal
|
||||||
|
|
||||||
Logic:
|
Logic:
|
||||||
Internal - not requestable by users, at all. Covers Corp_, Alliance_, Members etc groups.
|
Internal - not requestable by users, at all. Covers Corp_, Alliance_,
|
||||||
Groups are internal by default
|
Members etc groups. Groups are internal by default
|
||||||
|
|
||||||
Public - Other options are respected, but any user will be able to become and remain a member, even if they
|
Public - Other options are respected, but any user will be able to become
|
||||||
have no API etc entered. Auth will not manage these groups automatically so user removal is up to
|
and remain a member, even if they have no API etc entered.
|
||||||
|
Auth will not manage these groups automatically so user removal is up to
|
||||||
group managers/leaders.
|
group managers/leaders.
|
||||||
|
|
||||||
Not Internal and:
|
Not Internal and:
|
||||||
@@ -75,60 +96,119 @@ class AuthGroup(models.Model):
|
|||||||
Not Open - Users requests must be approved before they are added to the group
|
Not Open - Users requests must be approved before they are added to the group
|
||||||
"""
|
"""
|
||||||
group = models.OneToOneField(Group, on_delete=models.CASCADE, primary_key=True)
|
group = models.OneToOneField(Group, on_delete=models.CASCADE, primary_key=True)
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text=_(
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>"
|
||||||
|
"Auth will not remove users from this group automatically when they "
|
||||||
|
"are no longer authenticated."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
group_leaders = models.ManyToManyField(
|
||||||
|
User,
|
||||||
|
related_name='leads_groups',
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"Group leaders can process requests for this group. "
|
||||||
|
"Use the <code>auth.group_management</code> permission to allow "
|
||||||
|
"a user to manage all groups.<br>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
group_leader_groups = models.ManyToManyField(
|
||||||
|
Group,
|
||||||
|
related_name='leads_group_groups',
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"Members of leader groups can process requests for this group. "
|
||||||
|
"Use the <code>auth.group_management</code> permission "
|
||||||
|
"to allow a user to manage all groups.<br>")
|
||||||
|
)
|
||||||
|
states = models.ManyToManyField(
|
||||||
|
State,
|
||||||
|
related_name='valid_states',
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"States listed here will have the ability to join this group provided "
|
||||||
|
"they have the proper permissions.<br>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
description = models.TextField(
|
||||||
|
max_length=512,
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"Short description <i>(max. 512 characters)</i> "
|
||||||
|
"of the group shown to users."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
internal = models.BooleanField(default=True,
|
class Meta:
|
||||||
help_text="Internal group, users cannot see, join or request to join this group.<br>"
|
permissions = (
|
||||||
"Used for groups such as Members, Corp_*, Alliance_* etc.<br>"
|
("request_groups", _("Can request non-public groups")),
|
||||||
"<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.")
|
default_permissions = tuple()
|
||||||
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.")
|
|
||||||
public = models.BooleanField(default=False,
|
|
||||||
help_text="Group is public. Any registered user is able to join this group, with "
|
|
||||||
"visibility based on the other options set for this group.<br> Auth will "
|
|
||||||
"not remove users from this group automatically when they are no longer "
|
|
||||||
"authenticated.")
|
|
||||||
# Group leaders have management access to this group
|
|
||||||
group_leaders = models.ManyToManyField(User, related_name='leads_groups', 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.")
|
|
||||||
# allow groups to be *group leads*
|
|
||||||
group_leader_groups = models.ManyToManyField(Group, related_name='leads_group_groups', 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.")
|
|
||||||
|
|
||||||
states = models.ManyToManyField(State, related_name='valid_states', blank=True,
|
|
||||||
help_text="States listed here will have the ability to join this group provided "
|
|
||||||
"they have the proper permissions.")
|
|
||||||
|
|
||||||
description = models.TextField(max_length=512, blank=True, help_text="Short description <i>(max. 512 characters)"
|
|
||||||
"</i> of the group shown to users.")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.group.name
|
return self.group.name
|
||||||
|
|
||||||
class Meta:
|
def group_request_approvers(self) -> Set[User]:
|
||||||
permissions = (
|
"""Return all users who can approve a group request."""
|
||||||
("request_groups", "Can request non-public groups"),
|
return set(
|
||||||
|
self.group_leaders.all()
|
||||||
|
| User.objects.filter(groups__in=list(self.group_leader_groups.all()))
|
||||||
)
|
)
|
||||||
default_permissions = tuple()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Group)
|
class ReservedGroupName(models.Model):
|
||||||
def create_auth_group(sender, instance, created, **kwargs):
|
"""Name that can not be used for groups.
|
||||||
"""
|
|
||||||
Creates the AuthGroup model when a group is created
|
|
||||||
"""
|
|
||||||
if created:
|
|
||||||
AuthGroup.objects.create(group=instance)
|
|
||||||
|
|
||||||
|
This enables AA to ignore groups on other services (e.g. Discord) with that name.
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
_('name'),
|
||||||
|
max_length=150,
|
||||||
|
unique=True,
|
||||||
|
help_text=_("Name that can not be used for groups.")
|
||||||
|
)
|
||||||
|
reason = models.TextField(
|
||||||
|
_('reason'), help_text=_("Reason why this name is reserved.")
|
||||||
|
)
|
||||||
|
created_by = models.CharField(
|
||||||
|
_('created by'),
|
||||||
|
max_length=255,
|
||||||
|
help_text="Name of the user who created this entry."
|
||||||
|
)
|
||||||
|
created_at = models.DateTimeField(
|
||||||
|
_('created at'), default=now, help_text=_("Date when this entry was created")
|
||||||
|
)
|
||||||
|
|
||||||
@receiver(post_save, sender=Group)
|
def __str__(self) -> str:
|
||||||
def save_auth_group(sender, instance, **kwargs):
|
return self.name
|
||||||
"""
|
|
||||||
Ensures AuthGroup model is saved automatically
|
def save(self, *args, **kwargs) -> None:
|
||||||
"""
|
if Group.objects.filter(name__iexact=self.name).exists():
|
||||||
instance.authgroup.save()
|
raise RuntimeError(
|
||||||
|
f"Save failed. There already exists a group with the name: {self.name}."
|
||||||
|
)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|||||||
@@ -1,11 +1,33 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
from django.db.models.signals import pre_save, post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from allianceauth.authentication.signals import state_changed
|
from allianceauth.authentication.signals import state_changed
|
||||||
|
|
||||||
|
from .models import AuthGroup, ReservedGroupName
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(pre_save, sender=Group)
|
||||||
|
def find_new_name_for_conflicting_groups(sender, instance, **kwargs):
|
||||||
|
"""Find new name for a group which name is already reserved."""
|
||||||
|
new_name = instance.name
|
||||||
|
num = 0
|
||||||
|
while ReservedGroupName.objects.filter(name__iexact=new_name).exists():
|
||||||
|
num += 1
|
||||||
|
new_name = f"{instance.name}_{num}"
|
||||||
|
instance.name = new_name
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=Group)
|
||||||
|
def create_auth_group(sender, instance, created, **kwargs):
|
||||||
|
"""Create the AuthGroup model when a group is created."""
|
||||||
|
if created:
|
||||||
|
AuthGroup.objects.create(group=instance)
|
||||||
|
|
||||||
|
|
||||||
@receiver(state_changed)
|
@receiver(state_changed)
|
||||||
def check_groups_on_state_change(sender, user, state, **kwargs):
|
def check_groups_on_state_change(sender, user, state, **kwargs):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|||||||
@@ -127,6 +127,8 @@
|
|||||||
],
|
],
|
||||||
bootstrap: true
|
bootstrap: true
|
||||||
},
|
},
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -104,7 +104,9 @@
|
|||||||
"sortable": false,
|
"sortable": false,
|
||||||
"targets": [2]
|
"targets": [2]
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -29,15 +29,18 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a data-toggle="tab" href="#leave">
|
|
||||||
{% translate "Leave Requests" %}
|
|
||||||
|
|
||||||
{% if leaverequests %}
|
{% if not auto_leave %}
|
||||||
<span class="badge">{{ leaverequests|length }}</span>
|
<li>
|
||||||
{% endif %}
|
<a data-toggle="tab" href="#leave">
|
||||||
</a>
|
{% translate "Leave Requests" %}
|
||||||
</li>
|
|
||||||
|
{% if leaverequests %}
|
||||||
|
<span class="badge">{{ leaverequests|length }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="panel panel-default panel-tabs-aa">
|
<div class="panel panel-default panel-tabs-aa">
|
||||||
@@ -100,61 +103,63 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="leave" class="tab-pane">
|
{% if not auto_leave %}
|
||||||
{% if leaverequests %}
|
<div id="leave" class="tab-pane">
|
||||||
<div class="table-responsive">
|
{% if leaverequests %}
|
||||||
<table class="table table-aa">
|
<div class="table-responsive">
|
||||||
<thead>
|
<table class="table table-aa">
|
||||||
<tr>
|
<thead>
|
||||||
<th>{% translate "Character" %}</th>
|
|
||||||
<th>{% translate "Organization" %}</th>
|
|
||||||
<th>{% translate "Group" %}</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{% for leaverequest in leaverequests %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<th>{% translate "Character" %}</th>
|
||||||
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;">
|
<th>{% translate "Organization" %}</th>
|
||||||
{% if leaverequest.main_char %}
|
<th>{% translate "Group" %}</th>
|
||||||
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
<th></th>
|
||||||
{{ 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-right">
|
|
||||||
<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>
|
</tr>
|
||||||
{% endfor %}
|
</thead>
|
||||||
</tbody>
|
|
||||||
</table>
|
<tbody>
|
||||||
</div>
|
{% for leaverequest in leaverequests %}
|
||||||
{% else %}
|
<tr>
|
||||||
<div class="alert alert-warning text-center">{% translate "No group leave requests." %}</div>
|
<td>
|
||||||
{% endif %}
|
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;">
|
||||||
</div>
|
{% 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-right">
|
||||||
|
<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="alert alert-warning text-center">{% translate "No group leave requests." %}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ from allianceauth.authentication.models import CharacterOwnership, State
|
|||||||
from allianceauth.eveonline.models import (
|
from allianceauth.eveonline.models import (
|
||||||
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..admin import HasLeaderFilter, GroupAdmin, Group
|
from ..admin import HasLeaderFilter, GroupAdmin, Group
|
||||||
from . import get_admin_change_view_url
|
from . import get_admin_change_view_url
|
||||||
|
from ..models import ReservedGroupName
|
||||||
|
|
||||||
|
|
||||||
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||||
_has_auto_groups = True
|
_has_auto_groups = True
|
||||||
@@ -396,3 +397,108 @@ class TestGroupAdmin(TestCase):
|
|||||||
c.login(username='superuser', password='secret')
|
c.login(username='superuser', password='secret')
|
||||||
response = c.get(get_admin_change_view_url(self.group_1))
|
response = c.get(get_admin_change_view_url(self.group_1))
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_should_create_new_group(self):
|
||||||
|
# given
|
||||||
|
user = User.objects.create_superuser("bruce")
|
||||||
|
self.client.force_login(user)
|
||||||
|
# when
|
||||||
|
response = self.client.post(
|
||||||
|
"/admin/groupmanagement/group/add/",
|
||||||
|
data={
|
||||||
|
"name": "new group",
|
||||||
|
"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/")
|
||||||
|
self.assertTrue(Group.objects.filter(name="new group").exists())
|
||||||
|
|
||||||
|
def test_should_not_allow_creating_new_group_with_reserved_name(self):
|
||||||
|
# given
|
||||||
|
ReservedGroupName.objects.create(
|
||||||
|
name="new group", reason="dummy", created_by="bruce"
|
||||||
|
)
|
||||||
|
user = User.objects.create_superuser("bruce")
|
||||||
|
self.client.force_login(user)
|
||||||
|
# when
|
||||||
|
response = self.client.post(
|
||||||
|
"/admin/groupmanagement/group/add/",
|
||||||
|
data={
|
||||||
|
"name": "New group",
|
||||||
|
"authgroup-TOTAL_FORMS": 1,
|
||||||
|
"authgroup-INITIAL_FORMS": 0,
|
||||||
|
"authgroup-MIN_NUM_FORMS": 0,
|
||||||
|
"authgroup-MAX_NUM_FORMS": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# then
|
||||||
|
self.assertContains(
|
||||||
|
response, "This name has been reserved and can not be used for groups"
|
||||||
|
)
|
||||||
|
self.assertFalse(Group.objects.filter(name="new group").exists())
|
||||||
|
|
||||||
|
def test_should_not_allow_changing_name_of_existing_group_to_reserved_name(self):
|
||||||
|
# given
|
||||||
|
ReservedGroupName.objects.create(
|
||||||
|
name="new group", reason="dummy", created_by="bruce"
|
||||||
|
)
|
||||||
|
group = Group.objects.create(name="dummy")
|
||||||
|
user = User.objects.create_superuser("bruce")
|
||||||
|
self.client.force_login(user)
|
||||||
|
# when
|
||||||
|
response = self.client.post(
|
||||||
|
f"/admin/groupmanagement/group/{group.pk}/change/",
|
||||||
|
data={
|
||||||
|
"name": "new group",
|
||||||
|
"authgroup-TOTAL_FORMS": 1,
|
||||||
|
"authgroup-INITIAL_FORMS": 0,
|
||||||
|
"authgroup-MIN_NUM_FORMS": 0,
|
||||||
|
"authgroup-MAX_NUM_FORMS": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# then
|
||||||
|
self.assertContains(
|
||||||
|
response, "This name has been reserved and can not be used for groups"
|
||||||
|
)
|
||||||
|
self.assertFalse(Group.objects.filter(name="new group").exists())
|
||||||
|
|
||||||
|
|
||||||
|
class TestReservedGroupNameAdmin(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.user = User.objects.create_superuser("bruce")
|
||||||
|
|
||||||
|
def test_should_create_new_entry(self):
|
||||||
|
# given
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
# when
|
||||||
|
response = self.client.post(
|
||||||
|
"/admin/groupmanagement/reservedgroupname/add/",
|
||||||
|
data={"name": "Test", "reason": "dummy"}
|
||||||
|
)
|
||||||
|
# then
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, "/admin/groupmanagement/reservedgroupname/")
|
||||||
|
obj = ReservedGroupName.objects.get(name="test")
|
||||||
|
self.assertEqual(obj.name, "test")
|
||||||
|
self.assertEqual(obj.created_by, self.user.username)
|
||||||
|
self.assertTrue(obj.created_at)
|
||||||
|
|
||||||
|
def test_should_not_allow_names_of_existing_groups(self):
|
||||||
|
# given
|
||||||
|
Group.objects.create(name="Already taken")
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
# when
|
||||||
|
response = self.client.post(
|
||||||
|
"/admin/groupmanagement/reservedgroupname/add/",
|
||||||
|
data={"name": "already taken", "reason": "dummy"}
|
||||||
|
)
|
||||||
|
# then
|
||||||
|
self.assertContains(response, "There already exists a group with that name")
|
||||||
|
self.assertFalse(ReservedGroupName.objects.filter(name="already taken").exists())
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
from unittest.mock import Mock, patch
|
|
||||||
|
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo
|
from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
@@ -44,9 +41,9 @@ class GroupManagementVisibilityTestCase(TestCase):
|
|||||||
self._refresh_user()
|
self._refresh_user()
|
||||||
groups = GroupManager.get_group_leaders_groups(self.user)
|
groups = GroupManager.get_group_leaders_groups(self.user)
|
||||||
|
|
||||||
self.assertIn(self.group1, groups) #avail due to user
|
self.assertIn(self.group1, groups) # avail due to user
|
||||||
self.assertNotIn(self.group2, groups) #not avail due to group
|
self.assertNotIn(self.group2, groups) # not avail due to group
|
||||||
self.assertNotIn(self.group3, groups) #not avail at all
|
self.assertNotIn(self.group3, groups) # not avail at all
|
||||||
|
|
||||||
self.user.groups.add(self.group1)
|
self.user.groups.add(self.group1)
|
||||||
self._refresh_user()
|
self._refresh_user()
|
||||||
@@ -71,70 +68,66 @@ class GroupManagementVisibilityTestCase(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class TestGroupManager(TestCase):
|
class TestGroupManager(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super().setUpClass()
|
|
||||||
|
|
||||||
# group 1
|
# group 1
|
||||||
cls.group_default = Group.objects.create(name='default')
|
self.group_default = Group.objects.create(name='default')
|
||||||
cls.group_default.authgroup.description = 'Default Group'
|
self.group_default.authgroup.description = 'Default Group'
|
||||||
cls.group_default.authgroup.internal = False
|
self.group_default.authgroup.internal = False
|
||||||
cls.group_default.authgroup.hidden = False
|
self.group_default.authgroup.hidden = False
|
||||||
cls.group_default.authgroup.save()
|
self.group_default.authgroup.save()
|
||||||
|
|
||||||
# group 2
|
# group 2
|
||||||
cls.group_internal = Group.objects.create(name='internal')
|
self.group_internal = Group.objects.create(name='internal')
|
||||||
cls.group_internal.authgroup.description = 'Internal Group'
|
self.group_internal.authgroup.description = 'Internal Group'
|
||||||
cls.group_internal.authgroup.internal = True
|
self.group_internal.authgroup.internal = True
|
||||||
cls.group_internal.authgroup.save()
|
self.group_internal.authgroup.save()
|
||||||
|
|
||||||
# group 3
|
# group 3
|
||||||
cls.group_hidden = Group.objects.create(name='hidden')
|
self.group_hidden = Group.objects.create(name='hidden')
|
||||||
cls.group_hidden.authgroup.description = 'Hidden Group'
|
self.group_hidden.authgroup.description = 'Hidden Group'
|
||||||
cls.group_hidden.authgroup.internal = False
|
self.group_hidden.authgroup.internal = False
|
||||||
cls.group_hidden.authgroup.hidden = True
|
self.group_hidden.authgroup.hidden = True
|
||||||
cls.group_hidden.authgroup.save()
|
self.group_hidden.authgroup.save()
|
||||||
|
|
||||||
# group 4
|
# group 4
|
||||||
cls.group_open = Group.objects.create(name='open')
|
self.group_open = Group.objects.create(name='open')
|
||||||
cls.group_open.authgroup.description = 'Open Group'
|
self.group_open.authgroup.description = 'Open Group'
|
||||||
cls.group_open.authgroup.internal = False
|
self.group_open.authgroup.internal = False
|
||||||
cls.group_open.authgroup.hidden = False
|
self.group_open.authgroup.hidden = False
|
||||||
cls.group_open.authgroup.open = True
|
self.group_open.authgroup.open = True
|
||||||
cls.group_open.authgroup.save()
|
self.group_open.authgroup.save()
|
||||||
|
|
||||||
# group 5
|
# group 5
|
||||||
cls.group_public_1 = Group.objects.create(name='public 1')
|
self.group_public_1 = Group.objects.create(name='public 1')
|
||||||
cls.group_public_1.authgroup.description = 'Public Group 1'
|
self.group_public_1.authgroup.description = 'Public Group 1'
|
||||||
cls.group_public_1.authgroup.internal = False
|
self.group_public_1.authgroup.internal = False
|
||||||
cls.group_public_1.authgroup.hidden = False
|
self.group_public_1.authgroup.hidden = False
|
||||||
cls.group_public_1.authgroup.public = True
|
self.group_public_1.authgroup.public = True
|
||||||
cls.group_public_1.authgroup.save()
|
self.group_public_1.authgroup.save()
|
||||||
|
|
||||||
# group 6
|
# group 6
|
||||||
cls.group_public_2 = Group.objects.create(name='public 2')
|
self.group_public_2 = Group.objects.create(name='public 2')
|
||||||
cls.group_public_2.authgroup.description = 'Public Group 2'
|
self.group_public_2.authgroup.description = 'Public Group 2'
|
||||||
cls.group_public_2.authgroup.internal = False
|
self.group_public_2.authgroup.internal = False
|
||||||
cls.group_public_2.authgroup.hidden = True
|
self.group_public_2.authgroup.hidden = True
|
||||||
cls.group_public_2.authgroup.open = True
|
self.group_public_2.authgroup.open = True
|
||||||
cls.group_public_2.authgroup.public = True
|
self.group_public_2.authgroup.public = True
|
||||||
cls.group_public_2.authgroup.save()
|
self.group_public_2.authgroup.save()
|
||||||
|
|
||||||
# group 7
|
# group 7
|
||||||
cls.group_default_member = Group.objects.create(name='default members')
|
self.group_default_member = Group.objects.create(name='default members')
|
||||||
cls.group_default_member.authgroup.description = \
|
self.group_default_member.authgroup.description = \
|
||||||
'Default Group for members only'
|
'Default Group for members only'
|
||||||
cls.group_default_member.authgroup.internal = False
|
self.group_default_member.authgroup.internal = False
|
||||||
cls.group_default_member.authgroup.hidden = False
|
self.group_default_member.authgroup.hidden = False
|
||||||
cls.group_default_member.authgroup.open = False
|
self.group_default_member.authgroup.open = False
|
||||||
cls.group_default_member.authgroup.public = False
|
self.group_default_member.authgroup.public = False
|
||||||
cls.group_default_member.authgroup.states.add(
|
self.group_default_member.authgroup.states.add(
|
||||||
AuthUtils.get_member_state()
|
AuthUtils.get_member_state()
|
||||||
)
|
)
|
||||||
cls.group_default_member.authgroup.save()
|
self.group_default_member.authgroup.save()
|
||||||
|
|
||||||
def setUp(self):
|
# user
|
||||||
self.user = AuthUtils.create_user('Bruce Wayne')
|
self.user = AuthUtils.create_user('Bruce Wayne')
|
||||||
|
|
||||||
def test_get_joinable_group_member(self):
|
def test_get_joinable_group_member(self):
|
||||||
@@ -241,7 +234,7 @@ class TestGroupManager(TestCase):
|
|||||||
|
|
||||||
def test_get_joinable_groups_for_user_member_w_permission(self):
|
def test_get_joinable_groups_for_user_member_w_permission(self):
|
||||||
AuthUtils.assign_state(self.user, AuthUtils.get_member_state(), True)
|
AuthUtils.assign_state(self.user, AuthUtils.get_member_state(), True)
|
||||||
AuthUtils.add_permission_to_user_by_name(
|
self.user = AuthUtils.add_permission_to_user_by_name(
|
||||||
'groupmanagement.request_groups', self.user
|
'groupmanagement.request_groups', self.user
|
||||||
)
|
)
|
||||||
result = GroupManager.get_joinable_groups_for_user(self.user)
|
result = GroupManager.get_joinable_groups_for_user(self.user)
|
||||||
@@ -257,7 +250,7 @@ class TestGroupManager(TestCase):
|
|||||||
|
|
||||||
def test_get_joinable_groups_for_user_member_w_permission_no_hidden(self):
|
def test_get_joinable_groups_for_user_member_w_permission_no_hidden(self):
|
||||||
AuthUtils.assign_state(self.user, AuthUtils.get_member_state(), True)
|
AuthUtils.assign_state(self.user, AuthUtils.get_member_state(), True)
|
||||||
AuthUtils.add_permission_to_user_by_name(
|
self.user = AuthUtils.add_permission_to_user_by_name(
|
||||||
'groupmanagement.request_groups', self.user
|
'groupmanagement.request_groups', self.user
|
||||||
)
|
)
|
||||||
result = GroupManager.get_joinable_groups_for_user(
|
result = GroupManager.get_joinable_groups_for_user(
|
||||||
@@ -273,7 +266,7 @@ class TestGroupManager(TestCase):
|
|||||||
|
|
||||||
def test_has_management_permission(self):
|
def test_has_management_permission(self):
|
||||||
user = AuthUtils.create_user('Clark Kent')
|
user = AuthUtils.create_user('Clark Kent')
|
||||||
AuthUtils.add_permission_to_user_by_name(
|
user = AuthUtils.add_permission_to_user_by_name(
|
||||||
'auth.group_management', user
|
'auth.group_management', user
|
||||||
)
|
)
|
||||||
self.assertTrue(GroupManager.has_management_permission(user))
|
self.assertTrue(GroupManager.has_management_permission(user))
|
||||||
@@ -288,7 +281,7 @@ class TestGroupManager(TestCase):
|
|||||||
|
|
||||||
def test_can_manage_groups_has_perm(self):
|
def test_can_manage_groups_has_perm(self):
|
||||||
user = AuthUtils.create_user('Clark Kent')
|
user = AuthUtils.create_user('Clark Kent')
|
||||||
AuthUtils.add_permission_to_user_by_name(
|
user = AuthUtils.add_permission_to_user_by_name(
|
||||||
'auth.group_management', user
|
'auth.group_management', user
|
||||||
)
|
)
|
||||||
self.assertTrue(GroupManager.can_manage_groups(user))
|
self.assertTrue(GroupManager.can_manage_groups(user))
|
||||||
@@ -306,7 +299,7 @@ class TestGroupManager(TestCase):
|
|||||||
|
|
||||||
def test_can_manage_group_has_perm(self):
|
def test_can_manage_group_has_perm(self):
|
||||||
user = AuthUtils.create_user('Clark Kent')
|
user = AuthUtils.create_user('Clark Kent')
|
||||||
AuthUtils.add_permission_to_user_by_name(
|
user = AuthUtils.add_permission_to_user_by_name(
|
||||||
'auth.group_management', user
|
'auth.group_management', user
|
||||||
)
|
)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
@@ -433,11 +426,21 @@ class TestPendingRequestsCountForUser(TestCase):
|
|||||||
# when user_requestor is requesting access to group 1
|
# when user_requestor is requesting access to group 1
|
||||||
# then return 1 for user_leader_4
|
# then return 1 for user_leader_4
|
||||||
user_leader_4 = AuthUtils.create_member("Lex Luther")
|
user_leader_4 = AuthUtils.create_member("Lex Luther")
|
||||||
AuthUtils.add_permission_to_user_by_name("auth.group_management", user_leader_4)
|
user_leader_4 = AuthUtils.add_permission_to_user_by_name(
|
||||||
user_leader_4 = User.objects.get(pk=user_leader_4.pk)
|
"auth.group_management", user_leader_4
|
||||||
GroupRequest.objects.create(
|
|
||||||
user=self.user_requestor, group=self.group_1
|
|
||||||
)
|
)
|
||||||
|
GroupRequest.objects.create(user=self.user_requestor, group=self.group_1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
GroupManager.pending_requests_count_for_user(self.user_leader_1), 1
|
GroupManager.pending_requests_count_for_user(self.user_leader_1), 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_single_request_for_members_of_leading_group(self):
|
||||||
|
# given
|
||||||
|
leader_group = Group.objects.create(name="Leaders")
|
||||||
|
self.group_3.authgroup.group_leader_groups.add(leader_group)
|
||||||
|
self.user_leader_1.groups.add(leader_group)
|
||||||
|
GroupRequest.objects.create(user=self.user_requestor, group=self.group_3)
|
||||||
|
# when
|
||||||
|
result = GroupManager.pending_requests_count_for_user(self.user_leader_1)
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, 1)
|
||||||
|
|||||||
@@ -1,31 +1,22 @@
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import Group
|
||||||
from django.test import TestCase
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
from allianceauth.eveonline.models import (
|
|
||||||
EveCorporationInfo, EveAllianceInfo, EveCharacter
|
|
||||||
)
|
|
||||||
|
|
||||||
from ..models import GroupRequest, RequestLog
|
from ..models import GroupRequest, RequestLog, ReservedGroupName
|
||||||
|
|
||||||
|
MODULE_PATH = "allianceauth.groupmanagement.models"
|
||||||
|
|
||||||
|
|
||||||
def create_testdata():
|
def create_testdata():
|
||||||
# clear DB
|
|
||||||
User.objects.all().delete()
|
|
||||||
Group.objects.all().delete()
|
|
||||||
EveCharacter.objects.all().delete()
|
|
||||||
EveCorporationInfo.objects.all().delete()
|
|
||||||
EveAllianceInfo.objects.all().delete()
|
|
||||||
|
|
||||||
# group 1
|
# group 1
|
||||||
group = Group.objects.create(name='Superheros')
|
group = Group.objects.create(name='Superheros')
|
||||||
group.authgroup.description = 'Default Group'
|
group.authgroup.description = 'Default Group'
|
||||||
group.authgroup.internal = False
|
group.authgroup.internal = False
|
||||||
group.authgroup.hidden = False
|
group.authgroup.hidden = False
|
||||||
group.authgroup.save()
|
group.authgroup.save()
|
||||||
|
|
||||||
# user 1
|
# user 1
|
||||||
user_1 = AuthUtils.create_user('Bruce Wayne')
|
user_1 = AuthUtils.create_user('Bruce Wayne')
|
||||||
AuthUtils.add_main_character_2(
|
AuthUtils.add_main_character_2(
|
||||||
@@ -37,7 +28,6 @@ def create_testdata():
|
|||||||
)
|
)
|
||||||
user_1.groups.add(group)
|
user_1.groups.add(group)
|
||||||
group.authgroup.group_leaders.add(user_1)
|
group.authgroup.group_leaders.add(user_1)
|
||||||
|
|
||||||
# user 2
|
# user 2
|
||||||
user_2 = AuthUtils.create_user('Clark Kent')
|
user_2 = AuthUtils.create_user('Clark Kent')
|
||||||
AuthUtils.add_main_character_2(
|
AuthUtils.add_main_character_2(
|
||||||
@@ -45,18 +35,25 @@ def create_testdata():
|
|||||||
name='Clark Kent',
|
name='Clark Kent',
|
||||||
character_id=1002,
|
character_id=1002,
|
||||||
corp_id=2002,
|
corp_id=2002,
|
||||||
corp_name='Wayne Technologies'
|
corp_name='Wayne Food'
|
||||||
)
|
)
|
||||||
return group, user_1, user_2
|
# user 3
|
||||||
|
user_3 = AuthUtils.create_user('Peter Parker')
|
||||||
|
AuthUtils.add_main_character_2(
|
||||||
|
user_2,
|
||||||
|
name='Peter Parker',
|
||||||
|
character_id=1003,
|
||||||
|
corp_id=2002,
|
||||||
|
corp_name='Wayne Food'
|
||||||
|
)
|
||||||
|
return group, user_1, user_2, user_3
|
||||||
|
|
||||||
|
|
||||||
class TestGroupRequest(TestCase):
|
class TestGroupRequest(TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
cls.group, cls.user_1, _ = create_testdata()
|
cls.group, cls.user_1, cls.user_2, cls.user_3 = create_testdata()
|
||||||
|
|
||||||
def test_main_char(self):
|
def test_main_char(self):
|
||||||
group_request = GroupRequest.objects.create(
|
group_request = GroupRequest.objects.create(
|
||||||
@@ -74,13 +71,85 @@ class TestGroupRequest(TestCase):
|
|||||||
expected = 'Bruce Wayne:Superheros'
|
expected = 'Bruce Wayne:Superheros'
|
||||||
self.assertEqual(str(group_request), expected)
|
self.assertEqual(str(group_request), expected)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_REQUESTS_NOTIFICATION=True)
|
||||||
|
def test_should_notify_leaders_about_join_request(self):
|
||||||
|
# given
|
||||||
|
group_request = GroupRequest.objects.create(
|
||||||
|
user=self.user_2, group=self.group
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
with mock.patch(MODULE_PATH + ".notify") as mock_notify:
|
||||||
|
group_request.notify_leaders()
|
||||||
|
# then
|
||||||
|
self.assertTrue(mock_notify.called)
|
||||||
|
_, kwargs = mock_notify.call_args
|
||||||
|
self.assertEqual(kwargs["user"],self.user_1)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_REQUESTS_NOTIFICATION=True)
|
||||||
|
def test_should_notify_leaders_about_leave_request(self):
|
||||||
|
# given
|
||||||
|
group_request = GroupRequest.objects.create(
|
||||||
|
user=self.user_2, group=self.group
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
with mock.patch(MODULE_PATH + ".notify") as mock_notify:
|
||||||
|
group_request.notify_leaders()
|
||||||
|
# then
|
||||||
|
self.assertTrue(mock_notify.called)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_REQUESTS_NOTIFICATION=True)
|
||||||
|
def test_should_handle_notify_leaders_without_leaders(self):
|
||||||
|
# given
|
||||||
|
group = Group.objects.create(name='Dummy')
|
||||||
|
group.authgroup.internal = False
|
||||||
|
group.authgroup.hidden = False
|
||||||
|
group.authgroup.save()
|
||||||
|
group_request = GroupRequest.objects.create(
|
||||||
|
user=self.user_2, group=group
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
with mock.patch(MODULE_PATH + ".notify") as mock_notify:
|
||||||
|
group_request.notify_leaders()
|
||||||
|
# then
|
||||||
|
self.assertFalse(mock_notify.called)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_REQUESTS_NOTIFICATION=False)
|
||||||
|
def test_should_not_notify_leaders_if_disabled(self):
|
||||||
|
# given
|
||||||
|
group_request = GroupRequest.objects.create(
|
||||||
|
user=self.user_2, group=self.group
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
with mock.patch(MODULE_PATH + ".notify") as mock_notify:
|
||||||
|
group_request.notify_leaders()
|
||||||
|
# then
|
||||||
|
self.assertFalse(mock_notify.called)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_REQUESTS_NOTIFICATION=True)
|
||||||
|
def test_should_notify_members_of_leader_groups_about_join_request(self):
|
||||||
|
# given
|
||||||
|
child_group = Group.objects.create(name='Child')
|
||||||
|
child_group.authgroup.internal = False
|
||||||
|
child_group.authgroup.hidden = False
|
||||||
|
child_group.authgroup.save()
|
||||||
|
child_group.authgroup.group_leader_groups.add(self.group)
|
||||||
|
group_request = GroupRequest.objects.create(
|
||||||
|
user=self.user_2, group=child_group
|
||||||
|
)
|
||||||
|
# when
|
||||||
|
with mock.patch(MODULE_PATH + ".notify") as mock_notify:
|
||||||
|
group_request.notify_leaders()
|
||||||
|
# then
|
||||||
|
self.assertTrue(mock_notify.called)
|
||||||
|
_, kwargs = mock_notify.call_args
|
||||||
|
self.assertEqual(kwargs["user"],self.user_1)
|
||||||
|
|
||||||
|
|
||||||
class TestRequestLog(TestCase):
|
class TestRequestLog(TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
cls.group, cls.user_1, cls.user_2 = create_testdata()
|
cls.group, cls.user_1, cls.user_2, _ = create_testdata()
|
||||||
|
|
||||||
def test_requestor(self):
|
def test_requestor(self):
|
||||||
request_log = RequestLog.objects.create(
|
request_log = RequestLog.objects.create(
|
||||||
@@ -126,7 +195,7 @@ class TestRequestLog(TestCase):
|
|||||||
group=self.group,
|
group=self.group,
|
||||||
request_info='Clark Kent:Superheros',
|
request_info='Clark Kent:Superheros',
|
||||||
request_actor=self.user_1,
|
request_actor=self.user_1,
|
||||||
action = True
|
action=True
|
||||||
)
|
)
|
||||||
expected = 'Accept'
|
expected = 'Accept'
|
||||||
self.assertEqual(request_log.action_to_str(), expected)
|
self.assertEqual(request_log.action_to_str(), expected)
|
||||||
@@ -136,7 +205,7 @@ class TestRequestLog(TestCase):
|
|||||||
group=self.group,
|
group=self.group,
|
||||||
request_info='Clark Kent:Superheros',
|
request_info='Clark Kent:Superheros',
|
||||||
request_actor=self.user_1,
|
request_actor=self.user_1,
|
||||||
action = False
|
action=False
|
||||||
)
|
)
|
||||||
expected = 'Reject'
|
expected = 'Reject'
|
||||||
self.assertEqual(request_log.action_to_str(), expected)
|
self.assertEqual(request_log.action_to_str(), expected)
|
||||||
@@ -146,14 +215,13 @@ class TestRequestLog(TestCase):
|
|||||||
group=self.group,
|
group=self.group,
|
||||||
request_info='Clark Kent:Superheros',
|
request_info='Clark Kent:Superheros',
|
||||||
request_actor=self.user_1,
|
request_actor=self.user_1,
|
||||||
action = False
|
action=False
|
||||||
)
|
)
|
||||||
expected = self.user_2.profile.main_character
|
expected = self.user_2.profile.main_character
|
||||||
self.assertEqual(request_log.req_char(), expected)
|
self.assertEqual(request_log.req_char(), expected)
|
||||||
|
|
||||||
|
|
||||||
class TestAuthGroup(TestCase):
|
class TestAuthGroup(TestCase):
|
||||||
|
|
||||||
def test_str(self):
|
def test_str(self):
|
||||||
group = Group.objects.create(name='Superheros')
|
group = Group.objects.create(name='Superheros')
|
||||||
group.authgroup.description = 'Default Group'
|
group.authgroup.description = 'Default Group'
|
||||||
@@ -163,3 +231,75 @@ class TestAuthGroup(TestCase):
|
|||||||
|
|
||||||
expected = 'Superheros'
|
expected = 'Superheros'
|
||||||
self.assertEqual(str(group.authgroup), expected)
|
self.assertEqual(str(group.authgroup), expected)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAuthGroupRequestApprovers(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.group, self.user_1, self.user_2, self.user_3 = create_testdata()
|
||||||
|
|
||||||
|
def test_should_return_leaders_of_main_group_only(self):
|
||||||
|
# when
|
||||||
|
leaders = self.group.authgroup.group_request_approvers()
|
||||||
|
# then
|
||||||
|
self.assertSetEqual(leaders, {self.user_1})
|
||||||
|
|
||||||
|
def test_should_return_members_of_leading_groups_only(self):
|
||||||
|
# given
|
||||||
|
parent_group = Group.objects.create(name='Parent')
|
||||||
|
parent_group.authgroup.group_leaders.add(self.user_2)
|
||||||
|
self.user_1.groups.add(parent_group)
|
||||||
|
child_group = Group.objects.create(name='Child')
|
||||||
|
child_group.authgroup.internal = False
|
||||||
|
child_group.authgroup.hidden = False
|
||||||
|
child_group.authgroup.save()
|
||||||
|
child_group.authgroup.group_leader_groups.add(parent_group)
|
||||||
|
# when
|
||||||
|
leaders = child_group.authgroup.group_request_approvers()
|
||||||
|
# then
|
||||||
|
self.assertSetEqual(leaders, {self.user_1})
|
||||||
|
|
||||||
|
def test_should_return_leaders_of_main_group_and_members_of_leading_groups(self):
|
||||||
|
# given
|
||||||
|
parent_group = Group.objects.create(name='Parent')
|
||||||
|
parent_group.authgroup.group_leaders.add(self.user_2)
|
||||||
|
self.user_1.groups.add(parent_group)
|
||||||
|
child_group = Group.objects.create(name='Child')
|
||||||
|
child_group.authgroup.internal = False
|
||||||
|
child_group.authgroup.hidden = False
|
||||||
|
child_group.authgroup.save()
|
||||||
|
child_group.authgroup.group_leaders.add(self.user_3)
|
||||||
|
child_group.authgroup.group_leader_groups.add(self.group)
|
||||||
|
# when
|
||||||
|
leaders = child_group.authgroup.group_request_approvers()
|
||||||
|
# then
|
||||||
|
self.assertSetEqual(leaders, {self.user_1, self.user_3})
|
||||||
|
|
||||||
|
def test_can_handle_group_without_leaders(self):
|
||||||
|
# given
|
||||||
|
child_group = Group.objects.create(name='Child')
|
||||||
|
child_group.authgroup.internal = False
|
||||||
|
child_group.authgroup.hidden = False
|
||||||
|
child_group.authgroup.save()
|
||||||
|
# when
|
||||||
|
leaders = child_group.authgroup.group_request_approvers()
|
||||||
|
# then
|
||||||
|
self.assertSetEqual(leaders, set())
|
||||||
|
|
||||||
|
|
||||||
|
class TestReservedGroupName(TestCase):
|
||||||
|
def test_should_return_name(self):
|
||||||
|
# given
|
||||||
|
obj = ReservedGroupName(name="test", reason="abc", created_by="xxx")
|
||||||
|
# when
|
||||||
|
result = str(obj)
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, "test")
|
||||||
|
|
||||||
|
def test_should_not_allow_creating_reserved_name_for_existing_group(self):
|
||||||
|
# given
|
||||||
|
Group.objects.create(name="Dummy")
|
||||||
|
# when
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
ReservedGroupName.objects.create(
|
||||||
|
name="dummy", reason="abc", created_by="xxx"
|
||||||
|
)
|
||||||
|
|||||||
@@ -6,6 +6,27 @@ from allianceauth.eveonline.autogroups.models import AutogroupsConfig
|
|||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
|
||||||
|
|
||||||
|
from ..models import ReservedGroupName
|
||||||
|
|
||||||
|
|
||||||
|
class TestGroupSignals(TestCase):
|
||||||
|
def test_should_create_authgroup_when_group_is_created(self):
|
||||||
|
# when
|
||||||
|
group = Group.objects.create(name="test")
|
||||||
|
# then
|
||||||
|
self.assertEqual(group.authgroup.group, group)
|
||||||
|
|
||||||
|
def test_should_rename_group_that_conflicts_with_reserved_name(self):
|
||||||
|
# given
|
||||||
|
ReservedGroupName.objects.create(name="test", reason="dummy", created_by="xyz")
|
||||||
|
ReservedGroupName.objects.create(name="test_1", reason="dummy", created_by="xyz")
|
||||||
|
# when
|
||||||
|
group = Group.objects.create(name="Test")
|
||||||
|
# then
|
||||||
|
self.assertNotEqual(group.name, "test")
|
||||||
|
self.assertNotEqual(group.name, "test_1")
|
||||||
|
|
||||||
|
|
||||||
class TestCheckGroupsOnStateChange(TestCase):
|
class TestCheckGroupsOnStateChange(TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -1,22 +1,85 @@
|
|||||||
from unittest.mock import Mock, patch
|
from django.test import RequestFactory, TestCase, override_settings
|
||||||
|
|
||||||
from django.test import RequestFactory, TestCase
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
from esi.models import Token
|
|
||||||
|
|
||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
|
|
||||||
|
def response_content_to_str(response) -> str:
|
||||||
|
return response.content.decode(response.charset)
|
||||||
|
|
||||||
|
|
||||||
class TestViews(TestCase):
|
class TestViews(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.user = AuthUtils.create_user('Bruce Wayne')
|
self.user = AuthUtils.create_user('Peter Parker')
|
||||||
|
self.user_with_manage_permission = AuthUtils.create_user('Bruce Wayne')
|
||||||
|
|
||||||
|
# set permissions
|
||||||
|
AuthUtils.add_permission_to_user_by_name(
|
||||||
|
'auth.group_management', self.user_with_manage_permission
|
||||||
|
)
|
||||||
|
|
||||||
def test_groups_view_can_load(self):
|
def test_groups_view_can_load(self):
|
||||||
request = self.factory.get(reverse('groupmanagement:groups'))
|
request = self.factory.get(reverse('groupmanagement:groups'))
|
||||||
request.user = self.user
|
request.user = self.user
|
||||||
response = views.groups_view(request)
|
response = views.groups_view(request)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_management_view_can_load_for_user_with_permissions(self):
|
||||||
|
"""
|
||||||
|
Test if user with management permissions can access the management view
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user_with_manage_permission
|
||||||
|
response = views.group_management(request)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_management_view_doesnt_load_for_user_without_permissions(self):
|
||||||
|
"""
|
||||||
|
Test if user without management permissions can't access the management view
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user
|
||||||
|
response = views.group_management(request)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_AUTO_LEAVE=False)
|
||||||
|
def test_leave_requests_tab_visible(self):
|
||||||
|
"""
|
||||||
|
Test if the leave requests tab is visible when GROUPMANAGEMENT_AUTO_LEAVE = False
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user_with_manage_permission
|
||||||
|
response = views.group_management(request)
|
||||||
|
|
||||||
|
content = response_content_to_str(response)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertIn('<a data-toggle="tab" href="#leave">', content)
|
||||||
|
self.assertIn('<div id="leave" class="tab-pane">', content)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_AUTO_LEAVE=True)
|
||||||
|
def test_leave_requests_tab_hidden(self):
|
||||||
|
"""
|
||||||
|
Test if the leave requests tab is hidden when GROUPMANAGEMENT_AUTO_LEAVE = True
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user_with_manage_permission
|
||||||
|
response = views.group_management(request)
|
||||||
|
|
||||||
|
content = response_content_to_str(response)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertNotIn('<a data-toggle="tab" href="#leave">', content)
|
||||||
|
self.assertNotIn('<div id="leave" class="tab-pane">', content)
|
||||||
|
|||||||
@@ -45,7 +45,11 @@ def group_management(request):
|
|||||||
logger.debug("Providing user {} with {} acceptrequests and {} leaverequests.".format(
|
logger.debug("Providing user {} with {} acceptrequests and {} leaverequests.".format(
|
||||||
request.user, len(acceptrequests), len(leaverequests)))
|
request.user, len(acceptrequests), len(leaverequests)))
|
||||||
|
|
||||||
render_items = {'acceptrequests': acceptrequests, 'leaverequests': leaverequests}
|
render_items = {
|
||||||
|
'acceptrequests': acceptrequests,
|
||||||
|
'leaverequests': leaverequests,
|
||||||
|
'auto_leave': getattr(settings, 'GROUPMANAGEMENT_AUTO_LEAVE', False),
|
||||||
|
}
|
||||||
|
|
||||||
return render(request, 'groupmanagement/index.html', context=render_items)
|
return render(request, 'groupmanagement/index.html', context=render_items)
|
||||||
|
|
||||||
@@ -359,6 +363,7 @@ def group_request_add(request, group_id):
|
|||||||
grouprequest.leave_request = False
|
grouprequest.leave_request = False
|
||||||
grouprequest.save()
|
grouprequest.save()
|
||||||
logger.info(f"Created group request for user {request.user} to group {Group.objects.get(id=group_id)}")
|
logger.info(f"Created group request for user {request.user} to group {Group.objects.get(id=group_id)}")
|
||||||
|
grouprequest.notify_leaders()
|
||||||
messages.success(request, _('Applied to group %(group)s.') % {"group": group})
|
messages.success(request, _('Applied to group %(group)s.') % {"group": group})
|
||||||
return redirect("groupmanagement:groups")
|
return redirect("groupmanagement:groups")
|
||||||
|
|
||||||
@@ -387,7 +392,7 @@ def group_request_leave(request, group_id):
|
|||||||
logger.info(f"{request.user} attempted to leave {group} but already has an pending leave request.")
|
logger.info(f"{request.user} attempted to leave {group} but already has an pending leave request.")
|
||||||
messages.warning(request, _("You already have a pending leave request for that group."))
|
messages.warning(request, _("You already have a pending leave request for that group."))
|
||||||
return redirect("groupmanagement:groups")
|
return redirect("groupmanagement:groups")
|
||||||
if getattr(settings, 'AUTO_LEAVE', False):
|
if getattr(settings, 'GROUPMANAGEMENT_AUTO_LEAVE', False):
|
||||||
logger.info(f"{request.user} leaving joinable group {group} due to auto_leave")
|
logger.info(f"{request.user} leaving joinable group {group} due to auto_leave")
|
||||||
request_info = request.user.username + ":" + group.name
|
request_info = request.user.username + ":" + group.name
|
||||||
log = RequestLog(request_type=True, group=group, request_info=request_info, action=1, request_actor=request.user)
|
log = RequestLog(request_type=True, group=group, request_info=request_info, action=1, request_actor=request.user)
|
||||||
@@ -400,5 +405,6 @@ def group_request_leave(request, group_id):
|
|||||||
grouprequest.leave_request = True
|
grouprequest.leave_request = True
|
||||||
grouprequest.save()
|
grouprequest.save()
|
||||||
logger.info(f"Created group leave request for user {request.user} to group {Group.objects.get(id=group_id)}")
|
logger.info(f"Created group leave request for user {request.user} to group {Group.objects.get(id=group_id)}")
|
||||||
|
grouprequest.notify_leaders()
|
||||||
messages.success(request, _('Applied to leave group %(group)s.') % {"group": group})
|
messages.success(request, _('Applied to leave group %(group)s.') % {"group": group})
|
||||||
return redirect("groupmanagement:groups")
|
return redirect("groupmanagement:groups")
|
||||||
|
|||||||
Binary file not shown.
@@ -5,17 +5,17 @@
|
|||||||
#
|
#
|
||||||
# Translators:
|
# Translators:
|
||||||
# Erik Kalkoken <erik.kalkoken@gmail.com>, 2020
|
# Erik Kalkoken <erik.kalkoken@gmail.com>, 2020
|
||||||
# Peter Pfeufer <rounon.dax@terra-nanotech.de>, 2021
|
|
||||||
# Joel Falknau <ozirascal@gmail.com>, 2021
|
# Joel Falknau <ozirascal@gmail.com>, 2021
|
||||||
|
# Peter Pfeufer <rounon.dax@terra-nanotech.de>, 2021
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
||||||
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2021\n"
|
"Last-Translator: Peter Pfeufer <rounon.dax@terra-nanotech.de>, 2021\n"
|
||||||
"Language-Team: German (https://www.transifex.com/alliance-auth/teams/107430/de/)\n"
|
"Language-Team: German (https://www.transifex.com/alliance-auth/teams/107430/de/)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@@ -40,12 +40,12 @@ msgstr ""
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "E-Mail"
|
msgstr "E-Mail"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr "Status geändert zu %s"
|
msgstr "Status geändert zu %s"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr "Dein Nutzerstatus ist nun %(state)s"
|
msgstr "Dein Nutzerstatus ist nun %(state)s"
|
||||||
@@ -66,29 +66,29 @@ msgstr ""
|
|||||||
"\n"
|
"\n"
|
||||||
"Hauptcharakter (Status: %(state)s)"
|
"Hauptcharakter (Status: %(state)s)"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr "Kein Hauptcharakter gesetzt."
|
msgstr "Kein Hauptcharakter gesetzt."
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr "Charakter hinzufügen"
|
msgstr "Charakter hinzufügen"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr "Hauptcharakter ändern"
|
msgstr "Hauptcharakter ändern"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr "Gruppen"
|
msgstr "Gruppen"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr "Charaktere"
|
msgstr "Charaktere"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -97,13 +97,13 @@ msgstr "Charaktere"
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr "Corp"
|
msgstr "Corp"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -397,7 +397,7 @@ msgstr "Benutzername"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -476,7 +476,6 @@ msgstr "Flotte"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -485,8 +484,8 @@ msgstr "Ersteller"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Dauer"
|
msgstr "Dauer"
|
||||||
|
|
||||||
@@ -571,11 +570,128 @@ msgstr "Flottenteilnahme registriert."
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr "FAT-Link ist abgelaufen."
|
msgstr "FAT-Link ist abgelaufen."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
"Dieser Name ist reserviert und kann nicht als Gruppenname genutzt werden."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr "(automatisch)"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr "Es existiert bereits eine Gruppe mit diesem Namen."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr "Gruppenverwaltung"
|
msgstr "Gruppenverwaltung"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
"Interne Gruppe. Nutzer können diese nicht sehen und dieser nicht beitreten. "
|
||||||
|
"<br>Dies ist für Gruppen genutzt wie Mitglieder, Corp_*, Allianz_*, "
|
||||||
|
"etc.<br><b>Überschreibt die Versteckt und Offen Option wenn gesetzt</b>"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
"Diese Gruppe ist nicht sichtbar, aber Nutzer können dennoch beitreten wenn "
|
||||||
|
"diese den Link hierzu haben."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
"Gruppe ist offen und Nutzer werden dieser automatisch hinzugefügt bei "
|
||||||
|
"Anfrage.<br>Wenn die Gruppe nicht offen ist, müssen Anfragen manuell "
|
||||||
|
"bestätigt werden."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
"Öffentliche Gruppe. Jeder registrierte Nutzer kann dieser Gruppe beitreten, "
|
||||||
|
"he nach gesetzter Sichtbarkeit in den andern Optionen dieser Gruppe.<br>Auth"
|
||||||
|
" wird Nutzer nicht von dieser Gruppe entfernen wenn diese nicht länger "
|
||||||
|
"authentifiziert sind."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
"Gruppenleiter können Anfragen für diese Gruppe bearbeiten. Nutze die "
|
||||||
|
"<code>auth.group_management</code> Berechtigung um Nutzern zu erlauben alle "
|
||||||
|
"Gruppen zu verwalten<br>"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
"Mitglieder von Führungsgruppen können Anfragen für diese Gruppe bearbeiten. "
|
||||||
|
"Nutze die <code>auth.group_management</code> Berechtigung um Nutzern zu "
|
||||||
|
"erlauben alle Gruppen zu verwalten.<br>"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
"Hier gelistete Ränge können dieser Gruppe beitreten, vorausgesetzt sie haben"
|
||||||
|
" die entsprechenden Berechtigungen.<br>"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
"Kurze Beschreibung <i>(max. 512 Zeichen)</i> der Gruppe die dem Nutzer "
|
||||||
|
"angezeigt wird."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr "Kann nicht öffentlichen Gruppen beitreten"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr "Name kann nicht für Gruppen genutzt werden"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr "Grund"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr "Grund wieso dieser Name reserviert ist."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr "Erstellt bei"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr "Erstellt"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr "Datum der Erstellung dieses Eintrags"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -603,7 +719,7 @@ msgstr "Typ"
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -669,6 +785,7 @@ msgstr "Gruppen"
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Beschreibung"
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
@@ -865,24 +982,24 @@ msgstr "Du bist bereits Mitglied dieser Gruppe."
|
|||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr "Du hast Dich bereits für diese Gruppe beworben."
|
msgstr "Du hast Dich bereits für diese Gruppe beworben."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr "Beitritt zur Gruppe %(group)s beantragt."
|
msgstr "Beitritt zur Gruppe %(group)s beantragt."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr "Du kannst diese Gruppe nicht verlassen"
|
msgstr "Du kannst diese Gruppe nicht verlassen"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr "Du bist kein Mitglied dieser Gruppe"
|
msgstr "Du bist kein Mitglied dieser Gruppe"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr "Du hast bereits ein ausstehendes Austrittsgesuch für diese Gruppe."
|
msgstr "Du hast bereits ein ausstehendes Austrittsgesuch für diese Gruppe."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr "Austrittsgesuch für Gruppe %(group)s gesendet."
|
msgstr "Austrittsgesuch für Gruppe %(group)s gesendet."
|
||||||
@@ -1143,43 +1260,56 @@ msgstr "Alle gelesenen Benachrichtigungen gelöscht."
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr "Flottenoperationen"
|
msgstr "Flottenoperationen"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr "Doktrin"
|
msgstr "Doktrin"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr "Startzeit"
|
msgstr "Startzeit"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr "Operationsname"
|
msgstr "Operationsname"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr "Operationsart"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr "Flottenkommandeur"
|
msgstr "Flottenkommandeur"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr "Zusätzliche Info"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr "(Optinal) Beschreibe die Operation mit ein paar Worten"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr "Operation erstellen"
|
msgstr "Operation erstellen"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr "Form Up System"
|
msgstr "Form Up System"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr "Ortszeit"
|
msgstr "Ortszeit"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr "FC"
|
msgstr "FC"
|
||||||
|
|
||||||
@@ -1197,9 +1327,8 @@ msgid "Current Eve Time:"
|
|||||||
msgstr "Momentane Eve Zeit"
|
msgstr "Momentane Eve Zeit"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
msgstr "Anstehende Flottenoperationen"
|
||||||
msgstr "Nächste Timer"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
||||||
@@ -1207,9 +1336,8 @@ msgid "No upcoming timers."
|
|||||||
msgstr "Keine kommenden Timer."
|
msgstr "Keine kommenden Timer."
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
msgstr "Vergangene Flottenoperationen"
|
||||||
msgstr "Vergangene Timer"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
||||||
@@ -1226,17 +1354,17 @@ msgstr "Aktualisiere Flottenoperationen"
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr "Flottenoperation existiert nicht"
|
msgstr "Flottenoperation existiert nicht"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr "Operation timer für %(opname)s erstellt."
|
msgstr "Operation timer für %(opname)s erstellt."
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr "Operation timer für %(opname)s entfernt."
|
msgstr "Operation timer für %(opname)s entfernt."
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr "Änderungen für Operation timer %(opname)s gespeichert."
|
msgstr "Änderungen für Operation timer %(opname)s gespeichert."
|
||||||
@@ -1400,11 +1528,11 @@ msgstr "Passwort"
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr "Passwort muss mindestens 8 Zeichen lang sein"
|
msgstr "Passwort muss mindestens 8 Zeichen lang sein"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr "Discord Konto deaktiviert"
|
msgstr "Discord Konto deaktiviert"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1740,10 +1868,6 @@ msgstr "Flottenzeit"
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr "Flottendoktrin"
|
msgstr "Flottendoktrin"
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr "Zusätzliche Info"
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr "Killboard Link (zkillboard.com oder kb.evetools.org)"
|
msgstr "Killboard Link (zkillboard.com oder kb.evetools.org)"
|
||||||
@@ -1958,12 +2082,12 @@ msgstr ""
|
|||||||
"Der Killmail Link Deiner SRP Anfrage ist ungültig. Bitte stelle sicher, dass"
|
"Der Killmail Link Deiner SRP Anfrage ist ungültig. Bitte stelle sicher, dass"
|
||||||
" Du zKillboard benutzt."
|
" Du zKillboard benutzt."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr "SRP Anfrage für Deine %(ship)s gesendet."
|
msgstr "SRP Anfrage für Deine %(ship)s gesendet."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
@@ -1972,40 +2096,40 @@ msgstr ""
|
|||||||
"Charakter %(charid)s gehört nicht zu Deinem Auth Konto. Bitte füge den API "
|
"Charakter %(charid)s gehört nicht zu Deinem Auth Konto. Bitte füge den API "
|
||||||
"Key für diesen Charakter hinzu und versuche es erneut."
|
"Key für diesen Charakter hinzu und versuche es erneut."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr "Keine SRP Anfragen ausgewählt."
|
msgstr "Keine SRP Anfragen ausgewählt."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr "Ausgewählte SRP Anfrage konnte nicht gefunden werden."
|
msgstr "Ausgewählte SRP Anfrage konnte nicht gefunden werden."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr "%(numrequests)s SRP Anfragen gelöscht"
|
msgstr "%(numrequests)s SRP Anfragen gelöscht"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr "%(numrequests)s SRP Anfragen bestätigt."
|
msgstr "%(numrequests)s SRP Anfragen bestätigt."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr "Ausgewählte SRP Anfrage konnte nicht gefunden werden."
|
msgstr "Ausgewählte SRP Anfrage konnte nicht gefunden werden."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr "%(numrequests)s SRP Anfragen abgelehnt."
|
msgstr "%(numrequests)s SRP Anfragen abgelehnt."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr "Unfähig SRP Anfrage mit der ID %(requestid)s zu finden."
|
msgstr "Unfähig SRP Anfrage mit der ID %(requestid)s zu finden."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr "Änderungen der SRP Flotte %(fleetname)s gespeichert"
|
msgstr "Änderungen der SRP Flotte %(fleetname)s gespeichert"
|
||||||
@@ -2132,7 +2256,7 @@ msgstr "Strukturen Typ"
|
|||||||
|
|
||||||
#: allianceauth/timerboard/form.py:62
|
#: allianceauth/timerboard/form.py:62
|
||||||
msgid "Timer Type"
|
msgid "Timer Type"
|
||||||
msgstr ""
|
msgstr "Timer Typ"
|
||||||
|
|
||||||
#: allianceauth/timerboard/form.py:63
|
#: allianceauth/timerboard/form.py:63
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:33
|
#: allianceauth/timerboard/templates/timerboard/view.html:33
|
||||||
@@ -2163,15 +2287,15 @@ msgstr "Auf Corp beschränkt"
|
|||||||
|
|
||||||
#: allianceauth/timerboard/models.py:14
|
#: allianceauth/timerboard/models.py:14
|
||||||
msgid "Not Specified"
|
msgid "Not Specified"
|
||||||
msgstr ""
|
msgstr "Keine Angabe"
|
||||||
|
|
||||||
#: allianceauth/timerboard/models.py:15
|
#: allianceauth/timerboard/models.py:15
|
||||||
msgid "Shield"
|
msgid "Shield"
|
||||||
msgstr ""
|
msgstr "Schild"
|
||||||
|
|
||||||
#: allianceauth/timerboard/models.py:16
|
#: allianceauth/timerboard/models.py:16
|
||||||
msgid "Armor"
|
msgid "Armor"
|
||||||
msgstr ""
|
msgstr "Panzerung"
|
||||||
|
|
||||||
#: allianceauth/timerboard/models.py:17
|
#: allianceauth/timerboard/models.py:17
|
||||||
msgid "Hull"
|
msgid "Hull"
|
||||||
@@ -2179,15 +2303,15 @@ msgstr "Hülle"
|
|||||||
|
|
||||||
#: allianceauth/timerboard/models.py:18
|
#: allianceauth/timerboard/models.py:18
|
||||||
msgid "Final"
|
msgid "Final"
|
||||||
msgstr ""
|
msgstr "Final"
|
||||||
|
|
||||||
#: allianceauth/timerboard/models.py:19
|
#: allianceauth/timerboard/models.py:19
|
||||||
msgid "Anchoring"
|
msgid "Anchoring"
|
||||||
msgstr ""
|
msgstr "Ankernd"
|
||||||
|
|
||||||
#: allianceauth/timerboard/models.py:20
|
#: allianceauth/timerboard/models.py:20
|
||||||
msgid "Unanchoring"
|
msgid "Unanchoring"
|
||||||
msgstr ""
|
msgstr "Entankernd"
|
||||||
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/timer_confirm_delete.html:11
|
#: allianceauth/timerboard/templates/timerboard/timer_confirm_delete.html:11
|
||||||
msgid "Delete Timer"
|
msgid "Delete Timer"
|
||||||
@@ -2232,6 +2356,14 @@ msgstr "Corp Timer"
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr "Struktur"
|
msgstr "Struktur"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr "Nächste Timer"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr "Vergangene Timer"
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -34,12 +34,12 @@ msgstr ""
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -58,29 +58,29 @@ msgid ""
|
|||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -89,13 +89,13 @@ msgstr ""
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -380,7 +380,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -459,7 +459,6 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -468,8 +467,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -554,11 +553,104 @@ msgstr ""
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the <code>auth."
|
||||||
|
"group_management</code> permission to allow a user to manage all groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -586,7 +678,7 @@ msgstr ""
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -652,6 +744,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -844,24 +937,24 @@ msgstr ""
|
|||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1122,43 +1215,56 @@ msgstr ""
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1176,8 +1282,7 @@ msgid "Current Eve Time:"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
@@ -1186,8 +1291,7 @@ msgid "No upcoming timers."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
@@ -1205,17 +1309,17 @@ msgstr ""
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1379,11 +1483,11 @@ msgstr ""
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1706,10 +1810,6 @@ msgstr ""
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1919,52 +2019,52 @@ msgid ""
|
|||||||
"zKillboard."
|
"zKillboard."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
"API key for this character and try again"
|
"API key for this character and try again"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2191,6 +2291,14 @@ msgstr ""
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
||||||
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2021\n"
|
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2021\n"
|
||||||
"Language-Team: Spanish (https://www.transifex.com/alliance-auth/teams/107430/es/)\n"
|
"Language-Team: Spanish (https://www.transifex.com/alliance-auth/teams/107430/es/)\n"
|
||||||
@@ -39,12 +39,12 @@ msgstr ""
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "E-mail"
|
msgstr "E-mail"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -63,29 +63,29 @@ msgid ""
|
|||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr "No se ha seleccionado un personaje principal."
|
msgstr "No se ha seleccionado un personaje principal."
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr "Agregar Personaje"
|
msgstr "Agregar Personaje"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr "Cambiar Personaje Principal"
|
msgstr "Cambiar Personaje Principal"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr "Personajes"
|
msgstr "Personajes"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -94,13 +94,13 @@ msgstr "Personajes"
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nombre"
|
msgstr "Nombre"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr "Corporación"
|
msgstr "Corporación"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -391,7 +391,7 @@ msgstr "Usuario"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -470,7 +470,6 @@ msgstr "Flota"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -479,8 +478,8 @@ msgstr "Creador"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Duracion"
|
msgstr "Duracion"
|
||||||
|
|
||||||
@@ -565,11 +564,105 @@ msgstr "Participacion de flota registrada."
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr "Enlace de participacion expirado."
|
msgstr "Enlace de participacion expirado."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr "Manejo de Grupo"
|
msgstr "Manejo de Grupo"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -597,7 +690,7 @@ msgstr "Tipo"
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -663,6 +756,7 @@ msgstr "Grupos"
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Descripcion"
|
msgstr "Descripcion"
|
||||||
|
|
||||||
@@ -858,24 +952,24 @@ msgstr ""
|
|||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr "Solicitud enviada al grupo %(group)s."
|
msgstr "Solicitud enviada al grupo %(group)s."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr "No puedes dejar el grupos"
|
msgstr "No puedes dejar el grupos"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr "No eres miembro de ese grupo"
|
msgstr "No eres miembro de ese grupo"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr "Solicitaste dejar el grupo %(group)s."
|
msgstr "Solicitaste dejar el grupo %(group)s."
|
||||||
@@ -1136,43 +1230,56 @@ msgstr "Se borraron todas las notificaciones leidas."
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr "Operaciones de Flota"
|
msgstr "Operaciones de Flota"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr "Doctrina"
|
msgstr "Doctrina"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr "Tiempo de inicio"
|
msgstr "Tiempo de inicio"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr "Nombre de la operacion"
|
msgstr "Nombre de la operacion"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr "Comandante"
|
msgstr "Comandante"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr "Informacion Adicional"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr "Create Operacion"
|
msgstr "Create Operacion"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr "Sistema de encuentro"
|
msgstr "Sistema de encuentro"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr "Tiempo Local"
|
msgstr "Tiempo Local"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr "Comandante"
|
msgstr "Comandante"
|
||||||
|
|
||||||
@@ -1190,9 +1297,8 @@ msgid "Current Eve Time:"
|
|||||||
msgstr "Tipo en EVE actual:"
|
msgstr "Tipo en EVE actual:"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
msgstr ""
|
||||||
msgstr "Siguientes Timers"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
||||||
@@ -1200,9 +1306,8 @@ msgid "No upcoming timers."
|
|||||||
msgstr "No hay proximos timers."
|
msgstr "No hay proximos timers."
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
msgstr ""
|
||||||
msgstr "Timers Pasados"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
||||||
@@ -1219,17 +1324,17 @@ msgstr "Actualizar Operacion"
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr "La operacion no existe"
|
msgstr "La operacion no existe"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr "Se creo operacion para el timer %(opname)s."
|
msgstr "Se creo operacion para el timer %(opname)s."
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr "Se removio la operacion para %(opname)s."
|
msgstr "Se removio la operacion para %(opname)s."
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr "Se guardaron los cambios para la operacion %(opname)s"
|
msgstr "Se guardaron los cambios para la operacion %(opname)s"
|
||||||
@@ -1393,11 +1498,11 @@ msgstr "Contraseña"
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr "La contraseña tiene que tener 8 caracteres de largo minimo"
|
msgstr "La contraseña tiene que tener 8 caracteres de largo minimo"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1722,10 +1827,6 @@ msgstr "Hora de flota"
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr "Doctrina"
|
msgstr "Doctrina"
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr "Informacion Adicional"
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1936,52 +2037,52 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"El enalce suministrado no es valido. Por favor verifica si esats usando ZKB."
|
"El enalce suministrado no es valido. Por favor verifica si esats usando ZKB."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr "Solicitud de SRP para tu %(ship)s completo."
|
msgstr "Solicitud de SRP para tu %(ship)s completo."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
"API key for this character and try again"
|
"API key for this character and try again"
|
||||||
msgstr "El personaje %(charid)s no pertenece a tu cuenta"
|
msgstr "El personaje %(charid)s no pertenece a tu cuenta"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr "No se selecciono ninguna solicitud de SRP"
|
msgstr "No se selecciono ninguna solicitud de SRP"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr "Imposible localizar la solicitud de SRP."
|
msgstr "Imposible localizar la solicitud de SRP."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr "Se borraron %(numrequests)s pedidos de SRP"
|
msgstr "Se borraron %(numrequests)s pedidos de SRP"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr "Se aprobaron %(numrequests)s pedidos de SRP"
|
msgstr "Se aprobaron %(numrequests)s pedidos de SRP"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr "Imposible localizar el pedido de SRP"
|
msgstr "Imposible localizar el pedido de SRP"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr "Se rechazaron %(numrequests)s pedios de SRP."
|
msgstr "Se rechazaron %(numrequests)s pedios de SRP."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr "Imposible localizar la solicitud de SRP con ID %(requestid)s"
|
msgstr "Imposible localizar la solicitud de SRP con ID %(requestid)s"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr "Se guardaron los cambios en el SRP de la flota %(fleetname)s"
|
msgstr "Se guardaron los cambios en el SRP de la flota %(fleetname)s"
|
||||||
@@ -2208,6 +2309,14 @@ msgstr "Timers de Corporacion"
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr "Estructura"
|
msgstr "Estructura"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr "Siguientes Timers"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr "Timers Pasados"
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -4,17 +4,17 @@
|
|||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
#
|
#
|
||||||
# Translators:
|
# Translators:
|
||||||
# Linus Hope, 2021
|
|
||||||
# Alessandro Cresti, 2021
|
# Alessandro Cresti, 2021
|
||||||
|
# Linus Hope, 2021
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
||||||
"Last-Translator: Alessandro Cresti, 2021\n"
|
"Last-Translator: Linus Hope, 2021\n"
|
||||||
"Language-Team: Italian (Italy) (https://www.transifex.com/alliance-auth/teams/107430/it_IT/)\n"
|
"Language-Team: Italian (Italy) (https://www.transifex.com/alliance-auth/teams/107430/it_IT/)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@@ -40,12 +40,12 @@ msgstr ""
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -67,29 +67,29 @@ msgstr ""
|
|||||||
" Personaggio principale (State: %(state)s)\n"
|
" Personaggio principale (State: %(state)s)\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr "Nessun personaggio principale impostato"
|
msgstr "Nessun personaggio principale impostato"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr "Aggiungi personaggio"
|
msgstr "Aggiungi personaggio"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr "Cambia personaggio principale"
|
msgstr "Cambia personaggio principale"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr "Gruppi dei quali fai parte"
|
msgstr "Gruppi dei quali fai parte"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr "Personaggi"
|
msgstr "Personaggi"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -98,13 +98,13 @@ msgstr "Personaggi"
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nome"
|
msgstr "Nome"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr "Corporazione"
|
msgstr "Corporazione"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -117,7 +117,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/authentication/templates/public/register.html:7
|
#: allianceauth/authentication/templates/public/register.html:7
|
||||||
msgid "Registration"
|
msgid "Registration"
|
||||||
msgstr ""
|
msgstr "Iscriviti"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/public/register.html:22
|
#: allianceauth/authentication/templates/public/register.html:22
|
||||||
#: allianceauth/authentication/templates/registration/registration_form.html:5
|
#: allianceauth/authentication/templates/registration/registration_form.html:5
|
||||||
@@ -217,7 +217,7 @@ msgstr "Statistiche della corporazione"
|
|||||||
#: allianceauth/corputils/templates/corputils/base.html:3
|
#: allianceauth/corputils/templates/corputils/base.html:3
|
||||||
#: allianceauth/corputils/templates/corputils/base.html:6
|
#: allianceauth/corputils/templates/corputils/base.html:6
|
||||||
msgid "Corporation Member Data"
|
msgid "Corporation Member Data"
|
||||||
msgstr ""
|
msgstr "Informazioni sui membri della corporazione"
|
||||||
|
|
||||||
#: allianceauth/corputils/templates/corputils/base.html:12
|
#: allianceauth/corputils/templates/corputils/base.html:12
|
||||||
msgid "Corporations"
|
msgid "Corporations"
|
||||||
@@ -401,7 +401,7 @@ msgstr "Utente"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -480,7 +480,6 @@ msgstr "Flotta"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -489,8 +488,8 @@ msgstr "Autore"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Durata"
|
msgstr "Durata"
|
||||||
|
|
||||||
@@ -575,11 +574,105 @@ msgstr "Partecipazione alla flotta registrata."
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr "Il FAT link è scaduto."
|
msgstr "Il FAT link è scaduto."
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -607,7 +700,7 @@ msgstr "Tipo"
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -673,6 +766,7 @@ msgstr "Gruppi"
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Descrizione"
|
msgstr "Descrizione"
|
||||||
|
|
||||||
@@ -806,20 +900,20 @@ msgstr ""
|
|||||||
#: allianceauth/groupmanagement/views.py:159
|
#: allianceauth/groupmanagement/views.py:159
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed user %(user)s from group %(group)s."
|
msgid "Removed user %(user)s from group %(group)s."
|
||||||
msgstr ""
|
msgstr "Rimosso il membro %(user)s da %(group)s."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:161
|
#: allianceauth/groupmanagement/views.py:161
|
||||||
msgid "User does not exist in that group"
|
msgid "User does not exist in that group"
|
||||||
msgstr ""
|
msgstr "L’utente non fa parte del gruppo selezionato"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:164
|
#: allianceauth/groupmanagement/views.py:164
|
||||||
msgid "Group does not exist"
|
msgid "Group does not exist"
|
||||||
msgstr ""
|
msgstr "Il gruppo non esiste"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:191
|
#: allianceauth/groupmanagement/views.py:191
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||||
msgstr ""
|
msgstr "La domanda di %(mainchar)s per %(group)s è stata accettata."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:197
|
#: allianceauth/groupmanagement/views.py:197
|
||||||
#: allianceauth/groupmanagement/views.py:228
|
#: allianceauth/groupmanagement/views.py:228
|
||||||
@@ -828,16 +922,20 @@ msgid ""
|
|||||||
"An unhandled error occurred while processing the application from "
|
"An unhandled error occurred while processing the application from "
|
||||||
"%(mainchar)s to %(group)s."
|
"%(mainchar)s to %(group)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Si è verificato un’errore durante l’elaborazione della domanda di "
|
||||||
|
"%(mainchar)s per %(group)s."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:222
|
#: allianceauth/groupmanagement/views.py:222
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||||
msgstr ""
|
msgstr "La domanda di %(mainchar)s per %(group)s è stata rifiutata."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:257
|
#: allianceauth/groupmanagement/views.py:257
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"La domanda di congedo da parte di %(mainchar)s per %(group)s è stata "
|
||||||
|
"accettata."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:262
|
#: allianceauth/groupmanagement/views.py:262
|
||||||
#: allianceauth/groupmanagement/views.py:294
|
#: allianceauth/groupmanagement/views.py:294
|
||||||
@@ -846,91 +944,95 @@ msgid ""
|
|||||||
"An unhandled error occurred while processing the application from "
|
"An unhandled error occurred while processing the application from "
|
||||||
"%(mainchar)s to leave %(group)s."
|
"%(mainchar)s to leave %(group)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Si è verificato un’errore durante l’elaborazione della domanda di comgedo da"
|
||||||
|
" parte di %(mainchar)s per %(group)s."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:288
|
#: allianceauth/groupmanagement/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"La domanda di congedo da parte di %(mainchar)s per %(group)s è stata "
|
||||||
|
"rifiutata."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:332
|
#: allianceauth/groupmanagement/views.py:332
|
||||||
#: allianceauth/groupmanagement/views.py:342
|
#: allianceauth/groupmanagement/views.py:342
|
||||||
msgid "You cannot join that group"
|
msgid "You cannot join that group"
|
||||||
msgstr ""
|
msgstr "Non puoi aderire a questo gruppo"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:337
|
#: allianceauth/groupmanagement/views.py:337
|
||||||
msgid "You are already a member of that group."
|
msgid "You are already a member of that group."
|
||||||
msgstr ""
|
msgstr "Sei già parte del gruppo selezionato."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:354
|
#: allianceauth/groupmanagement/views.py:354
|
||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr ""
|
msgstr "La tua domanda per questo gruppo non è ancora stata valutata."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr ""
|
msgstr "Hai fatto domanda per il gruppo %(group)s."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr ""
|
msgstr "Non puoi lasciare questo gruppo."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr ""
|
msgstr "Non sei un membro di questo gruppo."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr ""
|
msgstr "La tua domanda di congedo non è ancora stata valutata."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr ""
|
msgstr "Hai fatto domanda di congedo per %(group)s."
|
||||||
|
|
||||||
#: allianceauth/hrapplications/auth_hooks.py:14
|
#: allianceauth/hrapplications/auth_hooks.py:14
|
||||||
msgid "Applications"
|
msgid "Applications"
|
||||||
msgstr ""
|
msgstr "Domande"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/forms.py:6
|
#: allianceauth/hrapplications/forms.py:6
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:92
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:92
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr ""
|
msgstr "Commenti"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/forms.py:10
|
#: allianceauth/hrapplications/forms.py:10
|
||||||
msgid "Search String"
|
msgid "Search String"
|
||||||
msgstr ""
|
msgstr "Stringa di ricerca"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:5
|
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:5
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:8
|
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:8
|
||||||
msgid "Choose a Corp"
|
msgid "Choose a Corp"
|
||||||
msgstr ""
|
msgstr "Seleziona una corporazione"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:11
|
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:11
|
||||||
msgid "Available Corps"
|
msgid "Available Corps"
|
||||||
msgstr ""
|
msgstr "Corporazioni disponibili"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:23
|
#: allianceauth/hrapplications/templates/hrapplications/corpchoice.html:23
|
||||||
msgid "No corps are accepting applications at this time."
|
msgid "No corps are accepting applications at this time."
|
||||||
msgstr ""
|
msgstr "Nessuna corporazione accetta domanda al momento."
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/create.html:5
|
#: allianceauth/hrapplications/templates/hrapplications/create.html:5
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/create.html:8
|
#: allianceauth/hrapplications/templates/hrapplications/create.html:8
|
||||||
msgid "Apply To"
|
msgid "Apply To"
|
||||||
msgstr ""
|
msgstr "Applica a"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:6
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:6
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:6
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:6
|
||||||
msgid "HR Application Management"
|
msgid "HR Application Management"
|
||||||
msgstr ""
|
msgstr "HR Risorse umane"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:11
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:11
|
||||||
msgid "Personal Applications"
|
msgid "Personal Applications"
|
||||||
msgstr ""
|
msgstr "Domande personali"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:15
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:15
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:18
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:18
|
||||||
msgid "Create Application"
|
msgid "Create Application"
|
||||||
msgstr ""
|
msgstr "Crea una domanda"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:26
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:26
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:80
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:80
|
||||||
@@ -938,7 +1040,7 @@ msgstr ""
|
|||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:24
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:24
|
||||||
#: allianceauth/services/templates/services/services.html:16
|
#: allianceauth/services/templates/services/services.html:16
|
||||||
msgid "Username"
|
msgid "Username"
|
||||||
msgstr ""
|
msgstr "Nome utente"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:29
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:29
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:84
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:84
|
||||||
@@ -948,7 +1050,7 @@ msgstr ""
|
|||||||
#: allianceauth/srp/templates/srp/data.html:103
|
#: allianceauth/srp/templates/srp/data.html:103
|
||||||
#: allianceauth/srp/templates/srp/management.html:46
|
#: allianceauth/srp/templates/srp/management.html:46
|
||||||
msgid "Actions"
|
msgid "Actions"
|
||||||
msgstr ""
|
msgstr "Azioni"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:39
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:39
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:100
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:100
|
||||||
@@ -957,7 +1059,7 @@ msgstr ""
|
|||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:16
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:16
|
||||||
#: allianceauth/srp/templates/srp/data.html:130
|
#: allianceauth/srp/templates/srp/data.html:130
|
||||||
msgid "Approved"
|
msgid "Approved"
|
||||||
msgstr ""
|
msgstr "Approvato"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:41
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:102
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:102
|
||||||
@@ -965,25 +1067,25 @@ msgstr ""
|
|||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:42
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:42
|
||||||
#: allianceauth/srp/templates/srp/data.html:134
|
#: allianceauth/srp/templates/srp/data.html:134
|
||||||
msgid "Rejected"
|
msgid "Rejected"
|
||||||
msgstr ""
|
msgstr "Rifiutato"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:61
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:61
|
||||||
msgid "Application Management"
|
msgid "Application Management"
|
||||||
msgstr ""
|
msgstr "Gestione delle domande"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:65
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:65
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:16
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:16
|
||||||
msgid "Search Applications"
|
msgid "Search Applications"
|
||||||
msgstr ""
|
msgstr "Cerca domande"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:71
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:71
|
||||||
msgid "Reviewed"
|
msgid "Reviewed"
|
||||||
msgstr ""
|
msgstr "Revisionato"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:79
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:79
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:123
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:123
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr ""
|
msgstr "Data"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:95
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:95
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:139
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:139
|
||||||
@@ -993,126 +1095,126 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:114
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:114
|
||||||
msgid "No pending applications."
|
msgid "No pending applications."
|
||||||
msgstr ""
|
msgstr "Nessuna domanda in sospeso."
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:163
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:163
|
||||||
msgid "No reviewed applications."
|
msgid "No reviewed applications."
|
||||||
msgstr ""
|
msgstr "Nessuna domanda revisionata."
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:177
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:177
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:63
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:63
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:135
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:135
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "Chiudi"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:178
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:178
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:64
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:64
|
||||||
msgid "Application Search"
|
msgid "Application Search"
|
||||||
msgstr ""
|
msgstr "Cerca domande"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:185
|
#: allianceauth/hrapplications/templates/hrapplications/management.html:185
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:71
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:71
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr ""
|
msgstr "Cerca"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:12
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:12
|
||||||
msgid "Application Search Results"
|
msgid "Application Search Results"
|
||||||
msgstr ""
|
msgstr "Risultati della tua ricerca domande"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:23
|
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:23
|
||||||
msgid "Application ID"
|
msgid "Application ID"
|
||||||
msgstr ""
|
msgstr "ID Domande"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:6
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:6
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:11
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:11
|
||||||
msgid "View Application"
|
msgid "View Application"
|
||||||
msgstr ""
|
msgstr "Visiona domanda"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:18
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:18
|
||||||
msgid "Denied"
|
msgid "Denied"
|
||||||
msgstr ""
|
msgstr "Accesso negato"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:28
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:28
|
||||||
msgid "Applicant"
|
msgid "Applicant"
|
||||||
msgstr ""
|
msgstr "Candidato"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:79
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:79
|
||||||
msgid "Approve"
|
msgid "Approve"
|
||||||
msgstr ""
|
msgstr "Approva"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:85
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:85
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr "Cancella"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:88
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:88
|
||||||
msgid "Mark in Progress"
|
msgid "Mark in Progress"
|
||||||
msgstr ""
|
msgstr "Segnala in elaborazione"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:102
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:102
|
||||||
#: allianceauth/services/forms.py:17
|
#: allianceauth/services/forms.py:17
|
||||||
msgid "Comments"
|
msgid "Comments"
|
||||||
msgstr ""
|
msgstr "Commenti"
|
||||||
|
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:137
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:137
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:144
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:144
|
||||||
msgid "Add Comment"
|
msgid "Add Comment"
|
||||||
msgstr ""
|
msgstr "Aggiungi commento"
|
||||||
|
|
||||||
#: allianceauth/notifications/models.py:21
|
#: allianceauth/notifications/models.py:21
|
||||||
msgid "danger"
|
msgid "danger"
|
||||||
msgstr ""
|
msgstr "pericolo"
|
||||||
|
|
||||||
#: allianceauth/notifications/models.py:22
|
#: allianceauth/notifications/models.py:22
|
||||||
msgid "warning"
|
msgid "warning"
|
||||||
msgstr ""
|
msgstr "attenzione"
|
||||||
|
|
||||||
#: allianceauth/notifications/models.py:23
|
#: allianceauth/notifications/models.py:23
|
||||||
msgid "info"
|
msgid "info"
|
||||||
msgstr ""
|
msgstr "informazioni"
|
||||||
|
|
||||||
#: allianceauth/notifications/models.py:24
|
#: allianceauth/notifications/models.py:24
|
||||||
msgid "success"
|
msgid "success"
|
||||||
msgstr ""
|
msgstr "successo"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:5
|
#: allianceauth/notifications/templates/notifications/list.html:5
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:9
|
#: allianceauth/notifications/templates/notifications/list.html:9
|
||||||
#: allianceauth/templates/allianceauth/notifications_menu_item.html:6
|
#: allianceauth/templates/allianceauth/notifications_menu_item.html:6
|
||||||
msgid "Notifications"
|
msgid "Notifications"
|
||||||
msgstr ""
|
msgstr "Notifiche"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:16
|
#: allianceauth/notifications/templates/notifications/list.html:16
|
||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
msgstr ""
|
msgstr "Non letto"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:18
|
#: allianceauth/notifications/templates/notifications/list.html:18
|
||||||
msgid "Read"
|
msgid "Read"
|
||||||
msgstr ""
|
msgstr "Letto"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:21
|
#: allianceauth/notifications/templates/notifications/list.html:21
|
||||||
msgid "Mark All Read"
|
msgid "Mark All Read"
|
||||||
msgstr ""
|
msgstr "Seleziona tutto visionato"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:22
|
#: allianceauth/notifications/templates/notifications/list.html:22
|
||||||
msgid "Delete All Read"
|
msgid "Delete All Read"
|
||||||
msgstr ""
|
msgstr "Cancella tutti i visionati"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:33
|
#: allianceauth/notifications/templates/notifications/list.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:63
|
#: allianceauth/notifications/templates/notifications/list.html:63
|
||||||
msgid "Timestamp"
|
msgid "Timestamp"
|
||||||
msgstr ""
|
msgstr "Ora"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:34
|
#: allianceauth/notifications/templates/notifications/list.html:34
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:64
|
#: allianceauth/notifications/templates/notifications/list.html:64
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr "Titolo"
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:53
|
#: allianceauth/notifications/templates/notifications/list.html:53
|
||||||
msgid "No unread notifications."
|
msgid "No unread notifications."
|
||||||
msgstr ""
|
msgstr "Nessuna notifica non visionata."
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:83
|
#: allianceauth/notifications/templates/notifications/list.html:83
|
||||||
msgid "No read notifications."
|
msgid "No read notifications."
|
||||||
msgstr ""
|
msgstr "Nessuna notifica visionata."
|
||||||
|
|
||||||
#: allianceauth/notifications/templates/notifications/view.html:5
|
#: allianceauth/notifications/templates/notifications/view.html:5
|
||||||
#: allianceauth/notifications/templates/notifications/view.html:11
|
#: allianceauth/notifications/templates/notifications/view.html:11
|
||||||
@@ -1143,43 +1245,56 @@ msgstr ""
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1197,8 +1312,7 @@ msgid "Current Eve Time:"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
@@ -1207,8 +1321,7 @@ msgid "No upcoming timers."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
@@ -1226,17 +1339,17 @@ msgstr ""
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1400,11 +1513,11 @@ msgstr ""
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1729,10 +1842,6 @@ msgstr ""
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1942,52 +2051,52 @@ msgid ""
|
|||||||
"zKillboard."
|
"zKillboard."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
"API key for this character and try again"
|
"API key for this character and try again"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2214,6 +2323,14 @@ msgstr ""
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
||||||
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2020\n"
|
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2020\n"
|
||||||
"Language-Team: Japanese (https://www.transifex.com/alliance-auth/teams/107430/ja/)\n"
|
"Language-Team: Japanese (https://www.transifex.com/alliance-auth/teams/107430/ja/)\n"
|
||||||
@@ -38,12 +38,12 @@ msgstr "実行するためにはメインキャラクターの設定が必要で
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "メールアドレス"
|
msgstr "メールアドレス"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr "分類が%sに変更されました。"
|
msgstr "分類が%sに変更されました。"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr "あなたの分類は%(state)sになりました。"
|
msgstr "あなたの分類は%(state)sになりました。"
|
||||||
@@ -64,29 +64,29 @@ msgstr ""
|
|||||||
"\n"
|
"\n"
|
||||||
" メインキャラクター(分類:%(state)s)"
|
" メインキャラクター(分類:%(state)s)"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr "メンキャラクターが選択されていません。"
|
msgstr "メンキャラクターが選択されていません。"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr "キャラクターを追加"
|
msgstr "キャラクターを追加"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr "メンキャラクターを変更"
|
msgstr "メンキャラクターを変更"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr "キャラクター"
|
msgstr "キャラクター"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -95,13 +95,13 @@ msgstr "キャラクター"
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "名前"
|
msgstr "名前"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr "Corp"
|
msgstr "Corp"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -386,7 +386,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -463,7 +463,6 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -472,8 +471,8 @@ msgstr "作成者"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "有効時間"
|
msgstr "有効時間"
|
||||||
|
|
||||||
@@ -558,11 +557,105 @@ msgstr "Fleet参加が登録されました。"
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr "Fat-Linkの有効期間が終了してます。"
|
msgstr "Fat-Linkの有効期間が終了してます。"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -590,7 +683,7 @@ msgstr ""
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -656,6 +749,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -848,24 +942,24 @@ msgstr "すでにその Group に参加してます。"
|
|||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr "すでに参加申請を送付済みです。"
|
msgstr "すでに参加申請を送付済みです。"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr "%(group)sへの参加申請を送信しました。"
|
msgstr "%(group)sへの参加申請を送信しました。"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr "この Group から脱退することはできません"
|
msgstr "この Group から脱退することはできません"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr "あなたはその Group のメンバーではありません"
|
msgstr "あなたはその Group のメンバーではありません"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr "すでに脱退申請を送信済みです。"
|
msgstr "すでに脱退申請を送信済みです。"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr "%(group)sからの脱退申請を送信しました。"
|
msgstr "%(group)sからの脱退申請を送信しました。"
|
||||||
@@ -1126,43 +1220,56 @@ msgstr "確認済みのすべての通知を削除"
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr "開始時間"
|
msgstr "開始時間"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr "作戦名"
|
msgstr "作戦名"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1180,8 +1287,7 @@ msgid "Current Eve Time:"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
@@ -1190,8 +1296,7 @@ msgid "No upcoming timers."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
@@ -1209,17 +1314,17 @@ msgstr ""
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr "%(opname)sのTimerが作成されました。"
|
msgstr "%(opname)sのTimerが作成されました。"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr "%(opname)sのTimerが削除されました。"
|
msgstr "%(opname)sのTimerが削除されました。"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr "%(opname)sのTimerの変更が保存されました。"
|
msgstr "%(opname)sのTimerの変更が保存されました。"
|
||||||
@@ -1383,11 +1488,11 @@ msgstr ""
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr "Passwordは8 文字以上必要です。"
|
msgstr "Passwordは8 文字以上必要です。"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr "Discordのアカウントを無効化"
|
msgstr "Discordのアカウントを無効化"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1712,10 +1817,6 @@ msgstr ""
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1928,52 +2029,52 @@ msgid ""
|
|||||||
"zKillboard."
|
"zKillboard."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
"API key for this character and try again"
|
"API key for this character and try again"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2199,6 +2300,14 @@ msgstr ""
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
||||||
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2020\n"
|
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2020\n"
|
||||||
"Language-Team: Korean (Korea) (https://www.transifex.com/alliance-auth/teams/107430/ko_KR/)\n"
|
"Language-Team: Korean (Korea) (https://www.transifex.com/alliance-auth/teams/107430/ko_KR/)\n"
|
||||||
@@ -41,12 +41,12 @@ msgstr "해당 기능을 수행하려면 주 캐릭터가 요구됨. 아래에
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "이메일"
|
msgstr "이메일"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr "상태가 %s로 변경됐습니다."
|
msgstr "상태가 %s로 변경됐습니다."
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr "사용자의 상태는 %(state)s입니다."
|
msgstr "사용자의 상태는 %(state)s입니다."
|
||||||
@@ -68,29 +68,29 @@ msgstr ""
|
|||||||
" 메인 캐릭터 (상태: %(state)s)\n"
|
" 메인 캐릭터 (상태: %(state)s)\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr "주 캐릭터가 지정되지 않음"
|
msgstr "주 캐릭터가 지정되지 않음"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr "캐릭터 추가"
|
msgstr "캐릭터 추가"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr "주 캐릭터 변경"
|
msgstr "주 캐릭터 변경"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr "그룹 멤버쉽"
|
msgstr "그룹 멤버쉽"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr "캐릭터"
|
msgstr "캐릭터"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -99,13 +99,13 @@ msgstr "캐릭터"
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "이름"
|
msgstr "이름"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr "콥"
|
msgstr "콥"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -390,7 +390,7 @@ msgstr "유저"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -467,7 +467,6 @@ msgstr "함대"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -476,8 +475,8 @@ msgstr "생성자"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "소요 시간"
|
msgstr "소요 시간"
|
||||||
|
|
||||||
@@ -562,11 +561,105 @@ msgstr "플릿 참여 등록됨"
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr "플릿활동추적 링크 기한만료"
|
msgstr "플릿활동추적 링크 기한만료"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr "그룹 관리"
|
msgstr "그룹 관리"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -594,7 +687,7 @@ msgstr "타입"
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -660,6 +753,7 @@ msgstr "그룹"
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "설명"
|
msgstr "설명"
|
||||||
|
|
||||||
@@ -852,24 +946,24 @@ msgstr "이미 해당 그룹에 가입되어 있습니다."
|
|||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr "해당 그룹에 대한 참여신청이 보류되었습니다."
|
msgstr "해당 그룹에 대한 참여신청이 보류되었습니다."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr "%(group)s그룹에 지원하였음."
|
msgstr "%(group)s그룹에 지원하였음."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr "해당 그룹을 떠날 수 없습니다."
|
msgstr "해당 그룹을 떠날 수 없습니다."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr "해당그룹의 멤버가 아닙니다."
|
msgstr "해당그룹의 멤버가 아닙니다."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr "해당 그룹의 탈퇴 신청이 접수된 상태입니다."
|
msgstr "해당 그룹의 탈퇴 신청이 접수된 상태입니다."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr "%(group)s그룹의 탈퇴가 신청됨."
|
msgstr "%(group)s그룹의 탈퇴가 신청됨."
|
||||||
@@ -1130,43 +1224,56 @@ msgstr "모든 읽은 알림을 삭제했습니다."
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr "플릿 옵"
|
msgstr "플릿 옵"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr "독트린"
|
msgstr "독트린"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr "시작 시간"
|
msgstr "시작 시간"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr "옵 이름"
|
msgstr "옵 이름"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr "플릿 커맨더"
|
msgstr "플릿 커맨더"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr "추가 기재 사항"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr "옵 만들기"
|
msgstr "옵 만들기"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr "폼업 성계"
|
msgstr "폼업 성계"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr "현지 시간"
|
msgstr "현지 시간"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr "FC"
|
msgstr "FC"
|
||||||
|
|
||||||
@@ -1184,9 +1291,8 @@ msgid "Current Eve Time:"
|
|||||||
msgstr "현재 이브 시간:"
|
msgstr "현재 이브 시간:"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
msgstr ""
|
||||||
msgstr "다음 옵 타이머"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
||||||
@@ -1194,9 +1300,8 @@ msgid "No upcoming timers."
|
|||||||
msgstr "예정된 옵 타이머가 없습니다."
|
msgstr "예정된 옵 타이머가 없습니다."
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
msgstr ""
|
||||||
msgstr "이전 옵 타이머"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
||||||
@@ -1213,17 +1318,17 @@ msgstr "플릿 옵 수정"
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr "존재하지 않는 플릿 옵"
|
msgstr "존재하지 않는 플릿 옵"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr "%(opname)s 의 옵 타이머를 생성했습니다."
|
msgstr "%(opname)s 의 옵 타이머를 생성했습니다."
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr "%(opname)s 의 옵 타이머를 제거했습니다."
|
msgstr "%(opname)s 의 옵 타이머를 제거했습니다."
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr "%(opname)s 의 옵 타이머 변경사항을 저장했습니다."
|
msgstr "%(opname)s 의 옵 타이머 변경사항을 저장했습니다."
|
||||||
@@ -1387,11 +1492,11 @@ msgstr "비밀번호"
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr "비밀번호는 8글자 이상이어야 합니다."
|
msgstr "비밀번호는 8글자 이상이어야 합니다."
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr "디스코드 계정 비활성화"
|
msgstr "디스코드 계정 비활성화"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1716,10 +1821,6 @@ msgstr "플릿 시간"
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr "플릿 독트린"
|
msgstr "플릿 독트린"
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr "추가 기재 사항"
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1929,12 +2030,12 @@ msgid ""
|
|||||||
"zKillboard."
|
"zKillboard."
|
||||||
msgstr "SRP 보상 요구를 위한 킬메일 링크가 유효하지 않습니다. zkillboard를 사용해 주십시요."
|
msgstr "SRP 보상 요구를 위한 킬메일 링크가 유효하지 않습니다. zkillboard를 사용해 주십시요."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr "%(ship)s에 대한 SRP 보상 요청이 제출되었습니다."
|
msgstr "%(ship)s에 대한 SRP 보상 요청이 제출되었습니다."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
@@ -1942,40 +2043,40 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"%(charid)s 캐릭터가 Auth 계정에 포함되어 있지 않습니다. 해당 캐릭터의 API를 추가하신 후, 다시 시도하시기 바랍니다."
|
"%(charid)s 캐릭터가 Auth 계정에 포함되어 있지 않습니다. 해당 캐릭터의 API를 추가하신 후, 다시 시도하시기 바랍니다."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr "SRP 보상 요청이 선택되지 않았습니다."
|
msgstr "SRP 보상 요청이 선택되지 않았습니다."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr "선택하신 SRP 보상 요청을 찾을 수 없습니다."
|
msgstr "선택하신 SRP 보상 요청을 찾을 수 없습니다."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr "SRP 보상 요청 %(numrequests)s 삭제 완료"
|
msgstr "SRP 보상 요청 %(numrequests)s 삭제 완료"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr "SRP 보상 요청 %(numrequests)s 승인 완료"
|
msgstr "SRP 보상 요청 %(numrequests)s 승인 완료"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr "선택하신 SRP 보상 요청을 찾을 수 없습니다."
|
msgstr "선택하신 SRP 보상 요청을 찾을 수 없습니다."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr "SRP 보상 요청 %(numrequests)s 거절됨."
|
msgstr "SRP 보상 요청 %(numrequests)s 거절됨."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr "SRP 보상 요청 %(requestid)s을 찾을 수 없습니다. "
|
msgstr "SRP 보상 요청 %(requestid)s을 찾을 수 없습니다. "
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr "SRP 보상 요청 플릿 %(fleetname)s의 변경 사항이 저장되었습니다."
|
msgstr "SRP 보상 요청 플릿 %(fleetname)s의 변경 사항이 저장되었습니다."
|
||||||
@@ -2201,6 +2302,14 @@ msgstr "콥 타이머"
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr "스트럭처"
|
msgstr "스트럭처"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr "다음 옵 타이머"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr "이전 옵 타이머"
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
||||||
"Last-Translator: Андрей Зубков <and.vareba81@gmail.com>, 2020\n"
|
"Last-Translator: Андрей Зубков <and.vareba81@gmail.com>, 2020\n"
|
||||||
"Language-Team: Russian (https://www.transifex.com/alliance-auth/teams/107430/ru/)\n"
|
"Language-Team: Russian (https://www.transifex.com/alliance-auth/teams/107430/ru/)\n"
|
||||||
@@ -39,12 +39,12 @@ msgstr "Необходимо указать основного персонаж
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr "Email"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr "Статус изменен: %s"
|
msgstr "Статус изменен: %s"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr "Статус пилота: %(state)s"
|
msgstr "Статус пилота: %(state)s"
|
||||||
@@ -66,29 +66,29 @@ msgstr ""
|
|||||||
" Основной персонаж (статус: %(state)s)\n"
|
" Основной персонаж (статус: %(state)s)\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr "Основной персонаж не установлен."
|
msgstr "Основной персонаж не установлен."
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr "Добавить Персонажа"
|
msgstr "Добавить Персонажа"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr "Сменить основного персонажа"
|
msgstr "Сменить основного персонажа"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr "Роли"
|
msgstr "Роли"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr "Персонажи"
|
msgstr "Персонажи"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -97,13 +97,13 @@ msgstr "Персонажи"
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Имя"
|
msgstr "Имя"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr "Корпорация"
|
msgstr "Корпорация"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -391,7 +391,7 @@ msgstr "Пользователь"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -474,7 +474,6 @@ msgstr "Флот"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -483,8 +482,8 @@ msgstr "Создатель"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Продолжительность"
|
msgstr "Продолжительность"
|
||||||
|
|
||||||
@@ -569,11 +568,105 @@ msgstr "Флотовое участие зарегистрированно."
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr "ФлАк ссылка устарела"
|
msgstr "ФлАк ссылка устарела"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr "Управление Группой"
|
msgstr "Управление Группой"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -601,7 +694,7 @@ msgstr "Тип"
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -667,6 +760,7 @@ msgstr "Группы"
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Описание"
|
msgstr "Описание"
|
||||||
|
|
||||||
@@ -863,24 +957,24 @@ msgstr "Вы уже участник этой группы."
|
|||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr "Вы уже подали заявку на вступление этой группы."
|
msgstr "Вы уже подали заявку на вступление этой группы."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr "Вступить в группу %(group)s."
|
msgstr "Вступить в группу %(group)s."
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr "Вы не можете покинуть эту группу"
|
msgstr "Вы не можете покинуть эту группу"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr "Вы не участник группыы"
|
msgstr "Вы не участник группыы"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr "Ваш запрос находится на рассмотрении"
|
msgstr "Ваш запрос находится на рассмотрении"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr "Запрос на выход из группы %(group)s."
|
msgstr "Запрос на выход из группы %(group)s."
|
||||||
@@ -1141,43 +1235,56 @@ msgstr "Удалить все прочитанные уведомления"
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr "Флотовые операции"
|
msgstr "Флотовые операции"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr "Доктрина"
|
msgstr "Доктрина"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr "Начало"
|
msgstr "Начало"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr "Название операции"
|
msgstr "Название операции"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr "ФлитКом"
|
msgstr "ФлитКом"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr "Дополнительная информация"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr "Создать операцию"
|
msgstr "Создать операцию"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr "Система сбора"
|
msgstr "Система сбора"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr "Локальное время"
|
msgstr "Локальное время"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr "ФК"
|
msgstr "ФК"
|
||||||
|
|
||||||
@@ -1195,9 +1302,8 @@ msgid "Current Eve Time:"
|
|||||||
msgstr "Текущий EVE Time:"
|
msgstr "Текущий EVE Time:"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
msgstr ""
|
||||||
msgstr "Следующие таймера"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
||||||
@@ -1205,9 +1311,8 @@ msgid "No upcoming timers."
|
|||||||
msgstr "Нет предстоящих таймеров"
|
msgstr "Нет предстоящих таймеров"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
msgstr ""
|
||||||
msgstr "Прошлые таймера"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
||||||
@@ -1224,17 +1329,17 @@ msgstr "Обновить Флотовые операции"
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr "Флотовая операция не существует"
|
msgstr "Флотовая операция не существует"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr "Таймер для %(opname)s назначен."
|
msgstr "Таймер для %(opname)s назначен."
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr "Таймер для %(opname)s удалено. "
|
msgstr "Таймер для %(opname)s удалено. "
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr "Таймер для %(opname)sобновлен."
|
msgstr "Таймер для %(opname)sобновлен."
|
||||||
@@ -1398,11 +1503,11 @@ msgstr "Пароль"
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr "Пароль должен быть не менее 8 символов."
|
msgstr "Пароль должен быть не менее 8 символов."
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr "Discord персонаж отключен"
|
msgstr "Discord персонаж отключен"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1735,10 +1840,6 @@ msgstr "Флотовое время"
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr "Флотовая Доктрина"
|
msgstr "Флотовая Доктрина"
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr "Дополнительная информация"
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1950,12 +2051,12 @@ msgstr ""
|
|||||||
"Ваш SRP запрос Killmail неправильный. Пожалуйста убедитесь в правильности "
|
"Ваш SRP запрос Killmail неправильный. Пожалуйста убедитесь в правильности "
|
||||||
"ссылки. "
|
"ссылки. "
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr "Запрос SRP на Ваш %(ship)s утвержден."
|
msgstr "Запрос SRP на Ваш %(ship)s утвержден."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
@@ -1964,40 +2065,40 @@ msgstr ""
|
|||||||
"Персонаж %(charid)s больше не имеет авторизации с Вашим аккаунтом. "
|
"Персонаж %(charid)s больше не имеет авторизации с Вашим аккаунтом. "
|
||||||
"Пожалуйста перепроверьте ключ доступа."
|
"Пожалуйста перепроверьте ключ доступа."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr "Нет SRP выбранных запросов"
|
msgstr "Нет SRP выбранных запросов"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr "Не могу найти выбранный SRP запрос."
|
msgstr "Не могу найти выбранный SRP запрос."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr "Удален %(numrequests)sиз SRP запросов."
|
msgstr "Удален %(numrequests)sиз SRP запросов."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr "Утвержден %(numrequests)s SRP запрос."
|
msgstr "Утвержден %(numrequests)s SRP запрос."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr "Невозможно найти выбранный SRP запрос"
|
msgstr "Невозможно найти выбранный SRP запрос"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr "SRP запрос %(numrequests)s отказано."
|
msgstr "SRP запрос %(numrequests)s отказано."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr "Невозможно найти SRP запрос с ID %(requestid)s."
|
msgstr "Невозможно найти SRP запрос с ID %(requestid)s."
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr "Сохранены изменения в SRP флот %(fleetname)s"
|
msgstr "Сохранены изменения в SRP флот %(fleetname)s"
|
||||||
@@ -2226,6 +2327,14 @@ msgstr "Корпоративные таймера"
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr "Структура"
|
msgstr "Структура"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr "Следующие таймера"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr "Прошлые таймера"
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-10-26 18:36+1000\n"
|
"POT-Creation-Date: 2021-11-29 01:03+1000\n"
|
||||||
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
|
||||||
"Last-Translator: Aaron BuBu <351793078@qq.com>, 2020\n"
|
"Last-Translator: Aaron BuBu <351793078@qq.com>, 2020\n"
|
||||||
"Language-Team: Chinese Simplified (https://www.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n"
|
"Language-Team: Chinese Simplified (https://www.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n"
|
||||||
@@ -39,12 +39,12 @@ msgstr "只有主要角色才能执行这个操作。在下面添加一个"
|
|||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "电子邮箱"
|
msgstr "电子邮箱"
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:74
|
#: allianceauth/authentication/models.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "State changed to: %s"
|
msgid "State changed to: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/models.py:75
|
#: allianceauth/authentication/models.py:80
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your user's state is now: %(state)s"
|
msgid "Your user's state is now: %(state)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -63,29 +63,29 @@ msgid ""
|
|||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:81
|
#: allianceauth/authentication/templates/authentication/dashboard.html:102
|
||||||
msgid "No main character set."
|
msgid "No main character set."
|
||||||
msgstr "没有主要角色组"
|
msgstr "没有主要角色组"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:88
|
#: allianceauth/authentication/templates/authentication/dashboard.html:109
|
||||||
msgid "Add Character"
|
msgid "Add Character"
|
||||||
msgstr "添加角色"
|
msgstr "添加角色"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:92
|
#: allianceauth/authentication/templates/authentication/dashboard.html:113
|
||||||
msgid "Change Main"
|
msgid "Change Main"
|
||||||
msgstr "修改主要角色"
|
msgstr "修改主要角色"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:101
|
#: allianceauth/authentication/templates/authentication/dashboard.html:122
|
||||||
msgid "Group Memberships"
|
msgid "Group Memberships"
|
||||||
msgstr "用户组成员"
|
msgstr "用户组成员"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:121
|
#: allianceauth/authentication/templates/authentication/dashboard.html:142
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:41
|
||||||
msgid "Characters"
|
msgid "Characters"
|
||||||
msgstr "角色"
|
msgstr "角色"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:129
|
#: allianceauth/authentication/templates/authentication/dashboard.html:150
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:73
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:24
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:15
|
||||||
@@ -94,13 +94,13 @@ msgstr "角色"
|
|||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "角色名"
|
msgstr "角色名"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:130
|
#: allianceauth/authentication/templates/authentication/dashboard.html:151
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:23
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:46
|
||||||
msgid "Corp"
|
msgid "Corp"
|
||||||
msgstr "所在公司"
|
msgstr "所在公司"
|
||||||
|
|
||||||
#: allianceauth/authentication/templates/authentication/dashboard.html:131
|
#: allianceauth/authentication/templates/authentication/dashboard.html:152
|
||||||
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
#: allianceauth/corputils/templates/corputils/corpstats.html:76
|
||||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
#: allianceauth/hrapplications/templates/hrapplications/view.html:47
|
||||||
msgid "Alliance"
|
msgid "Alliance"
|
||||||
@@ -385,7 +385,7 @@ msgstr "用户"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:25
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:29
|
||||||
#: allianceauth/optimer/form.py:7 allianceauth/timerboard/form.py:59
|
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
#: allianceauth/timerboard/templates/timerboard/view.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
#: allianceauth/timerboard/templates/timerboard/view.html:201
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
#: allianceauth/timerboard/templates/timerboard/view.html:374
|
||||||
@@ -462,7 +462,6 @@ msgstr "舰队"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:49
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:74
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:17
|
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
#: allianceauth/timerboard/templates/timerboard/view.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
#: allianceauth/timerboard/templates/timerboard/view.html:205
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
#: allianceauth/timerboard/templates/timerboard/view.html:378
|
||||||
@@ -471,8 +470,8 @@ msgstr "创建者"
|
|||||||
|
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html:51
|
||||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:77
|
||||||
#: allianceauth/optimer/form.py:9
|
#: allianceauth/optimer/form.py:18
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "持续时间"
|
msgstr "持续时间"
|
||||||
|
|
||||||
@@ -557,11 +556,105 @@ msgstr "成功注册舰队PAP"
|
|||||||
msgid "FAT link has expired."
|
msgid "FAT link has expired."
|
||||||
msgstr "PAP链接已过期"
|
msgstr "PAP链接已过期"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:104
|
||||||
|
msgid "This name has been reserved and can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:230
|
||||||
|
msgid "(auto)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/admin.py:239
|
||||||
|
msgid "There already exists a group with that name."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/auth_hooks.py:17
|
#: allianceauth/groupmanagement/auth_hooks.py:17
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/menu.html:14
|
||||||
msgid "Group Management"
|
msgid "Group Management"
|
||||||
msgstr "用户组管理"
|
msgstr "用户组管理"
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:102
|
||||||
|
msgid ""
|
||||||
|
"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>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:110
|
||||||
|
msgid "Group is hidden from users but can still join with the correct link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:116
|
||||||
|
msgid ""
|
||||||
|
"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."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:123
|
||||||
|
msgid ""
|
||||||
|
"Group is public. Any registered user is able to join this group, with "
|
||||||
|
"visibility based on the other options set for this group.<br>Auth will not "
|
||||||
|
"remove users from this group automatically when they are no longer "
|
||||||
|
"authenticated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:134
|
||||||
|
msgid ""
|
||||||
|
"Group leaders can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:144
|
||||||
|
msgid ""
|
||||||
|
"Members of leader groups can process requests for this group. Use the "
|
||||||
|
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||||
|
"groups.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:153
|
||||||
|
msgid ""
|
||||||
|
"States listed here will have the ability to join this group provided they "
|
||||||
|
"have the proper permissions.<br>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:161
|
||||||
|
msgid ""
|
||||||
|
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:168
|
||||||
|
msgid "Can request non-public groups"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:189
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:192
|
||||||
|
msgid "Name that can not be used for groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:195
|
||||||
|
msgid "Reason why this name is reserved."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:198
|
||||||
|
msgid "created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "created at"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/groupmanagement/models.py:203
|
||||||
|
msgid "Date when this entry was created"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:5
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:14
|
||||||
msgid "Audit Log"
|
msgid "Audit Log"
|
||||||
@@ -589,7 +682,7 @@ msgstr "类型"
|
|||||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:33
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:35
|
#: allianceauth/notifications/templates/notifications/list.html:35
|
||||||
#: allianceauth/notifications/templates/notifications/list.html:65
|
#: allianceauth/notifications/templates/notifications/list.html:65
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:18
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:19
|
||||||
#: allianceauth/services/templates/services/services.html:18
|
#: allianceauth/services/templates/services/services.html:18
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
#: allianceauth/timerboard/templates/timerboard/view.html:40
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
#: allianceauth/timerboard/templates/timerboard/view.html:207
|
||||||
@@ -655,6 +748,7 @@ msgstr "群组"
|
|||||||
|
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:25
|
||||||
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
|
||||||
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "描述"
|
msgstr "描述"
|
||||||
|
|
||||||
@@ -847,24 +941,24 @@ msgstr "你已经是那个群组的一员了。"
|
|||||||
msgid "You already have a pending application for that group."
|
msgid "You already have a pending application for that group."
|
||||||
msgstr "你已经有了该组的未决申请"
|
msgstr "你已经有了该组的未决申请"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:362
|
#: allianceauth/groupmanagement/views.py:363
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to group %(group)s."
|
msgid "Applied to group %(group)s."
|
||||||
msgstr "修改已经应用到%(group)s啦"
|
msgstr "修改已经应用到%(group)s啦"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:372
|
#: allianceauth/groupmanagement/views.py:373
|
||||||
msgid "You cannot leave that group"
|
msgid "You cannot leave that group"
|
||||||
msgstr "你无法离开那个用户组"
|
msgstr "你无法离开那个用户组"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:376
|
#: allianceauth/groupmanagement/views.py:377
|
||||||
msgid "You are not a member of that group"
|
msgid "You are not a member of that group"
|
||||||
msgstr "你不是那个用户组的成员"
|
msgstr "你不是那个用户组的成员"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:388
|
#: allianceauth/groupmanagement/views.py:389
|
||||||
msgid "You already have a pending leave request for that group."
|
msgid "You already have a pending leave request for that group."
|
||||||
msgstr "你已经有了该组的未决离开请求"
|
msgstr "你已经有了该组的未决离开请求"
|
||||||
|
|
||||||
#: allianceauth/groupmanagement/views.py:403
|
#: allianceauth/groupmanagement/views.py:405
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Applied to leave group %(group)s."
|
msgid "Applied to leave group %(group)s."
|
||||||
msgstr "已经离开群组%(group)s"
|
msgstr "已经离开群组%(group)s"
|
||||||
@@ -1125,43 +1219,56 @@ msgstr "删除所有已读通知"
|
|||||||
msgid "Fleet Operations"
|
msgid "Fleet Operations"
|
||||||
msgstr "起队搞事"
|
msgstr "起队搞事"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:6
|
#: allianceauth/optimer/form.py:12
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:10
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
||||||
msgid "Doctrine"
|
msgid "Doctrine"
|
||||||
msgstr "船型"
|
msgstr "船型"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:8
|
#: allianceauth/optimer/form.py:14
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
||||||
msgid "Start Time"
|
msgid "Start Time"
|
||||||
msgstr "集合时间"
|
msgstr "集合时间"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:10
|
#: allianceauth/optimer/form.py:15
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:9
|
||||||
msgid "Operation Name"
|
msgid "Operation Name"
|
||||||
msgstr "搞事名目"
|
msgstr "搞事名目"
|
||||||
|
|
||||||
#: allianceauth/optimer/form.py:11
|
#: allianceauth/optimer/form.py:16
|
||||||
|
msgid "Operation Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:17
|
||||||
#: allianceauth/srp/templates/srp/management.html:40
|
#: allianceauth/srp/templates/srp/management.html:40
|
||||||
msgid "Fleet Commander"
|
msgid "Fleet Commander"
|
||||||
msgstr "FC"
|
msgstr "FC"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:22 allianceauth/srp/form.py:14
|
||||||
|
#: allianceauth/srp/templates/srp/data.html:93
|
||||||
|
msgid "Additional Info"
|
||||||
|
msgstr "其他信息"
|
||||||
|
|
||||||
|
#: allianceauth/optimer/form.py:23
|
||||||
|
msgid "(Optional) Describe the operation with a couple of short words."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/add.html:7
|
#: allianceauth/optimer/templates/optimer/add.html:7
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:14
|
#: allianceauth/optimer/templates/optimer/management.html:14
|
||||||
msgid "Create Operation"
|
msgid "Create Operation"
|
||||||
msgstr "起一个队"
|
msgstr "起一个队"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:11
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:12
|
||||||
msgid "Form Up System"
|
msgid "Form Up System"
|
||||||
msgstr "集结点"
|
msgstr "集结点"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:13
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:14
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
#: allianceauth/timerboard/templates/timerboard/view.html:37
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
#: allianceauth/timerboard/templates/timerboard/view.html:204
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
#: allianceauth/timerboard/templates/timerboard/view.html:377
|
||||||
msgid "Local Time"
|
msgid "Local Time"
|
||||||
msgstr "本地时间"
|
msgstr "本地时间"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/fleetoptable.html:15
|
#: allianceauth/optimer/templates/optimer/fleetoptable.html:16
|
||||||
msgid "FC"
|
msgid "FC"
|
||||||
msgstr "FC"
|
msgstr "FC"
|
||||||
|
|
||||||
@@ -1179,9 +1286,8 @@ msgid "Current Eve Time:"
|
|||||||
msgstr "当前EVE游戏内时间"
|
msgstr "当前EVE游戏内时间"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:27
|
#: allianceauth/optimer/templates/optimer/management.html:27
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
msgid "Next Fleet Operations"
|
||||||
msgid "Next Timers"
|
msgstr ""
|
||||||
msgstr "接下来的时间节点"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:31
|
#: allianceauth/optimer/templates/optimer/management.html:31
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
#: allianceauth/timerboard/templates/timerboard/view.html:363
|
||||||
@@ -1189,9 +1295,8 @@ msgid "No upcoming timers."
|
|||||||
msgstr "没有快到的时间节点,歇一会吧"
|
msgstr "没有快到的时间节点,歇一会吧"
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:34
|
#: allianceauth/optimer/templates/optimer/management.html:34
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
msgid "Past Fleet Operations"
|
||||||
msgid "Past Timers"
|
msgstr ""
|
||||||
msgstr "已经过去的时间节点"
|
|
||||||
|
|
||||||
#: allianceauth/optimer/templates/optimer/management.html:38
|
#: allianceauth/optimer/templates/optimer/management.html:38
|
||||||
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
#: allianceauth/timerboard/templates/timerboard/view.html:536
|
||||||
@@ -1208,17 +1313,17 @@ msgstr "更新搞事队"
|
|||||||
msgid "Fleet Operation Does Not Exist"
|
msgid "Fleet Operation Does Not Exist"
|
||||||
msgstr "这搞事队不存在啊,你会不会搞事啊"
|
msgstr "这搞事队不存在啊,你会不会搞事啊"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:55
|
#: allianceauth/optimer/views.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Created operation timer for %(opname)s."
|
msgid "Created operation timer for %(opname)s."
|
||||||
msgstr "为%(opname)s创建了搞事时间节点,冲鸭"
|
msgstr "为%(opname)s创建了搞事时间节点,冲鸭"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:73
|
#: allianceauth/optimer/views.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Removed operation timer for %(opname)s."
|
msgid "Removed operation timer for %(opname)s."
|
||||||
msgstr "移除了%(opname)s的搞事时间节点,咕咕咕?"
|
msgstr "移除了%(opname)s的搞事时间节点,咕咕咕?"
|
||||||
|
|
||||||
#: allianceauth/optimer/views.py:96
|
#: allianceauth/optimer/views.py:125
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to operation timer for %(opname)s."
|
msgid "Saved changes to operation timer for %(opname)s."
|
||||||
msgstr "对搞事时间节点%(opname)s的修改保存了,朝令夕改你是不是合格FC啊?"
|
msgstr "对搞事时间节点%(opname)s的修改保存了,朝令夕改你是不是合格FC啊?"
|
||||||
@@ -1382,11 +1487,11 @@ msgstr "密码"
|
|||||||
msgid "Password must be at least 8 characters long."
|
msgid "Password must be at least 8 characters long."
|
||||||
msgstr "密码至少要有8个字符啊,你也太不注重安全啦"
|
msgstr "密码至少要有8个字符啊,你也太不注重安全啦"
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:225
|
#: allianceauth/services/modules/discord/models.py:234
|
||||||
msgid "Discord Account Disabled"
|
msgid "Discord Account Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: allianceauth/services/modules/discord/models.py:227
|
#: allianceauth/services/modules/discord/models.py:236
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your Discord account was disabled automatically by Auth. If you think this "
|
"Your Discord account was disabled automatically by Auth. If you think this "
|
||||||
"was a mistake, please contact an admin."
|
"was a mistake, please contact an admin."
|
||||||
@@ -1711,10 +1816,6 @@ msgstr "集结时间"
|
|||||||
msgid "Fleet Doctrine"
|
msgid "Fleet Doctrine"
|
||||||
msgstr "舰队船型"
|
msgstr "舰队船型"
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:14 allianceauth/srp/templates/srp/data.html:93
|
|
||||||
msgid "Additional Info"
|
|
||||||
msgstr "其他信息"
|
|
||||||
|
|
||||||
#: allianceauth/srp/form.py:16
|
#: allianceauth/srp/form.py:16
|
||||||
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
msgid "Killboard Link (zkillboard.com or kb.evetools.org)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1924,52 +2025,52 @@ msgid ""
|
|||||||
"zKillboard."
|
"zKillboard."
|
||||||
msgstr "小老弟,你这个补损用的KB链接不对劲儿啊,你是不是没用zKillboard啊?"
|
msgstr "小老弟,你这个补损用的KB链接不对劲儿啊,你是不是没用zKillboard啊?"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:211
|
#: allianceauth/srp/views.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Submitted SRP request for your %(ship)s."
|
msgid "Submitted SRP request for your %(ship)s."
|
||||||
msgstr "你的%(ship)s的补损申请好啦"
|
msgstr "你的%(ship)s的补损申请好啦"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:215
|
#: allianceauth/srp/views.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Character %(charid)s does not belong to your Auth account. Please add the "
|
"Character %(charid)s does not belong to your Auth account. Please add the "
|
||||||
"API key for this character and try again"
|
"API key for this character and try again"
|
||||||
msgstr "%(charid)s这个角色好像不在你的账号里啊,你交没交ESI啊?交过再试一次吧"
|
msgstr "%(charid)s这个角色好像不在你的账号里啊,你交没交ESI啊?交过再试一次吧"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:235 allianceauth/srp/views.py:261
|
#: allianceauth/srp/views.py:236 allianceauth/srp/views.py:262
|
||||||
#: allianceauth/srp/views.py:299
|
#: allianceauth/srp/views.py:300
|
||||||
msgid "No SRP requests selected"
|
msgid "No SRP requests selected"
|
||||||
msgstr "你没选中任何补损请求哦"
|
msgstr "你没选中任何补损请求哦"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:246 allianceauth/srp/views.py:284
|
#: allianceauth/srp/views.py:247 allianceauth/srp/views.py:285
|
||||||
msgid "Unable to locate selected SRP request."
|
msgid "Unable to locate selected SRP request."
|
||||||
msgstr "你选的这条补损请求找不到呀"
|
msgstr "你选的这条补损请求找不到呀"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:249
|
#: allianceauth/srp/views.py:250
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %(numrequests)s SRP requests"
|
msgid "Deleted %(numrequests)s SRP requests"
|
||||||
msgstr "删了%(numrequests)s条补损请求,你是不是准备赖账啊?"
|
msgstr "删了%(numrequests)s条补损请求,你是不是准备赖账啊?"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:287
|
#: allianceauth/srp/views.py:288
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Approved %(numrequests)s SRP requests"
|
msgid "Approved %(numrequests)s SRP requests"
|
||||||
msgstr "通过了%(numrequests)s条补损请求,钱包大出血?"
|
msgstr "通过了%(numrequests)s条补损请求,钱包大出血?"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:319
|
#: allianceauth/srp/views.py:320
|
||||||
msgid "Unable to locate selected SRP request"
|
msgid "Unable to locate selected SRP request"
|
||||||
msgstr "你选的这条补损请求找不到呀"
|
msgstr "你选的这条补损请求找不到呀"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:322
|
#: allianceauth/srp/views.py:323
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Rejected %(numrequests)s SRP requests."
|
msgid "Rejected %(numrequests)s SRP requests."
|
||||||
msgstr "已拒绝%(numrequests)s个补损申请,小老弟你这是想赖账?"
|
msgstr "已拒绝%(numrequests)s个补损申请,小老弟你这是想赖账?"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:335
|
#: allianceauth/srp/views.py:336
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Unable to locate SRP request with ID %(requestid)s"
|
msgid "Unable to locate SRP request with ID %(requestid)s"
|
||||||
msgstr "找不到ID是%(requestid)s的补损申请呀,老哥眼花了?"
|
msgstr "找不到ID是%(requestid)s的补损申请呀,老哥眼花了?"
|
||||||
|
|
||||||
#: allianceauth/srp/views.py:359
|
#: allianceauth/srp/views.py:360
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Saved changes to SRP fleet %(fleetname)s"
|
msgid "Saved changes to SRP fleet %(fleetname)s"
|
||||||
msgstr "你做的修改已经保存到%(fleetname)s这个补损舰队啦,尽情白给吧!"
|
msgstr "你做的修改已经保存到%(fleetname)s这个补损舰队啦,尽情白给吧!"
|
||||||
@@ -2195,6 +2296,14 @@ msgstr "公司时间表"
|
|||||||
msgid "Structure"
|
msgid "Structure"
|
||||||
msgstr "建筑"
|
msgstr "建筑"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:194
|
||||||
|
msgid "Next Timers"
|
||||||
|
msgstr "接下来的时间节点"
|
||||||
|
|
||||||
|
#: allianceauth/timerboard/templates/timerboard/view.html:367
|
||||||
|
msgid "Past Timers"
|
||||||
|
msgstr "已经过去的时间节点"
|
||||||
|
|
||||||
#: allianceauth/timerboard/views.py:74
|
#: allianceauth/timerboard/views.py:74
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added new timer in %(system)s at %(time)s."
|
msgid "Added new timer in %(system)s at %(time)s."
|
||||||
|
|||||||
@@ -49,19 +49,22 @@ class NotificationManager(models.Manager):
|
|||||||
logger.info("Created notification %s", obj)
|
logger.info("Created notification %s", obj)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def _max_notifications_per_user(self):
|
def _max_notifications_per_user(self) -> int:
|
||||||
"""return the maximum number of notifications allowed per user"""
|
"""Maximum number of notifications allowed per user."""
|
||||||
max_notifications = getattr(settings, 'NOTIFICATIONS_MAX_PER_USER', None)
|
max_notifications = getattr(
|
||||||
if (
|
settings,
|
||||||
max_notifications is None
|
"NOTIFICATIONS_MAX_PER_USER",
|
||||||
or not isinstance(max_notifications, int)
|
self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||||
or max_notifications < 0
|
)
|
||||||
):
|
try:
|
||||||
|
max_notifications = int(max_notifications)
|
||||||
|
except ValueError:
|
||||||
|
max_notifications = None
|
||||||
|
if max_notifications is None or max_notifications < 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'NOTIFICATIONS_MAX_PER_USER setting is invalid. Using default.'
|
"NOTIFICATIONS_MAX_PER_USER setting is invalid. Using default."
|
||||||
)
|
)
|
||||||
max_notifications = self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
max_notifications = self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||||
|
|
||||||
return max_notifications
|
return max_notifications
|
||||||
|
|
||||||
def user_unread_count(self, user_pk: int) -> int:
|
def user_unread_count(self, user_pk: int) -> int:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
@@ -113,29 +114,53 @@ class TestUserNotify(TestCase):
|
|||||||
self.assertSetEqual(result, expected)
|
self.assertSetEqual(result, expected)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("allianceauth.notifications.managers.logger")
|
||||||
@patch(
|
@patch(
|
||||||
MODULE_PATH + '.Notification.NOTIFICATIONS_MAX_PER_USER_DEFAULT',
|
MODULE_PATH + ".Notification.NOTIFICATIONS_MAX_PER_USER_DEFAULT",
|
||||||
NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||||
)
|
)
|
||||||
class TestMaxNotificationsPerUser(TestCase):
|
class TestMaxNotificationsPerUser(TestCase):
|
||||||
|
@override_settings(NOTIFICATIONS_MAX_PER_USER=42)
|
||||||
@override_settings(NOTIFICATIONS_MAX_PER_USER=None)
|
def test_should_use_custom_integer_setting(self, mock_logger):
|
||||||
def test_reset_to_default_if_not_defined(self):
|
# when
|
||||||
result = Notification.objects._max_notifications_per_user()
|
result = Notification.objects._max_notifications_per_user()
|
||||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
# then
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, 42)
|
||||||
|
self.assertFalse(mock_logger.warning.called)
|
||||||
|
|
||||||
@override_settings(NOTIFICATIONS_MAX_PER_USER='11')
|
@override_settings(NOTIFICATIONS_MAX_PER_USER="42")
|
||||||
def test_reset_to_default_if_not_int(self):
|
def test_should_use_custom_string_setting(self, mock_logger):
|
||||||
|
# when
|
||||||
result = Notification.objects._max_notifications_per_user()
|
result = Notification.objects._max_notifications_per_user()
|
||||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
# then
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, 42)
|
||||||
|
self.assertFalse(mock_logger.warning.called)
|
||||||
|
|
||||||
|
@override_settings()
|
||||||
|
def test_should_use_default_if_not_defined(self, mock_logger):
|
||||||
|
# given
|
||||||
|
del settings.NOTIFICATIONS_MAX_PER_USER
|
||||||
|
# when
|
||||||
|
result = Notification.objects._max_notifications_per_user()
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, NOTIFICATIONS_MAX_PER_USER_DEFAULT)
|
||||||
|
self.assertFalse(mock_logger.warning.called)
|
||||||
|
|
||||||
|
@override_settings(NOTIFICATIONS_MAX_PER_USER="abc")
|
||||||
|
def test_should_reset_to_default_if_not_int(self, mock_logger):
|
||||||
|
# when
|
||||||
|
result = Notification.objects._max_notifications_per_user()
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, NOTIFICATIONS_MAX_PER_USER_DEFAULT)
|
||||||
|
self.assertTrue(mock_logger.warning.called)
|
||||||
|
|
||||||
@override_settings(NOTIFICATIONS_MAX_PER_USER=-1)
|
@override_settings(NOTIFICATIONS_MAX_PER_USER=-1)
|
||||||
def test_reset_to_default_if_lt_zero(self):
|
def test_should_reset_to_default_if_lt_zero(self, mock_logger):
|
||||||
|
# when
|
||||||
result = Notification.objects._max_notifications_per_user()
|
result = Notification.objects._max_notifications_per_user()
|
||||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
# then
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, NOTIFICATIONS_MAX_PER_USER_DEFAULT)
|
||||||
|
self.assertTrue(mock_logger.warning.called)
|
||||||
|
|
||||||
|
|
||||||
@patch('allianceauth.notifications.managers.cache')
|
@patch('allianceauth.notifications.managers.cache')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from allianceauth.optimer.models import OpTimer
|
from allianceauth.optimer.models import OpTimer, OpTimerType
|
||||||
|
|
||||||
|
admin.site.register(OpTimerType)
|
||||||
admin.site.register(OpTimer)
|
admin.site.register(OpTimer)
|
||||||
|
|||||||
@@ -1,11 +1,34 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from allianceauth.optimer.form_widgets import DataListWidget
|
||||||
|
|
||||||
|
|
||||||
class OpForm(forms.Form):
|
class OpForm(forms.Form):
|
||||||
|
"""
|
||||||
|
Create/Edit Fleet Operation Form
|
||||||
|
"""
|
||||||
|
|
||||||
doctrine = forms.CharField(max_length=254, required=True, label=_('Doctrine'))
|
doctrine = forms.CharField(max_length=254, required=True, label=_('Doctrine'))
|
||||||
system = forms.CharField(max_length=254, required=True, label=_("System"))
|
system = forms.CharField(max_length=254, required=True, label=_("System"))
|
||||||
start = forms.DateTimeField(required=True, label=_("Start Time"))
|
start = forms.DateTimeField(required=True, label=_("Start Time"))
|
||||||
duration = forms.CharField(max_length=254, required=True, label=_("Duration"))
|
|
||||||
operation_name = forms.CharField(max_length=254, required=True, label=_("Operation Name"))
|
operation_name = forms.CharField(max_length=254, required=True, label=_("Operation Name"))
|
||||||
|
type = forms.CharField(required=False, label=_("Operation Type"))
|
||||||
fc = forms.CharField(max_length=254, required=True, label=_("Fleet Commander"))
|
fc = forms.CharField(max_length=254, required=True, label=_("Fleet Commander"))
|
||||||
|
duration = forms.CharField(max_length=254, required=True, label=_("Duration"))
|
||||||
|
description = forms.CharField(
|
||||||
|
widget=forms.Textarea(attrs={"rows": 10, "cols": 20, "input_type": "textarea"}),
|
||||||
|
required=False,
|
||||||
|
label=_("Additional Info"),
|
||||||
|
help_text=_("(Optional) Describe the operation with a couple of short words."),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
_data_list = kwargs.pop('data_list', None)
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Add the DataListWidget to our type field
|
||||||
|
self.fields['type'].widget = DataListWidget(
|
||||||
|
data_list=_data_list, name='data-list'
|
||||||
|
)
|
||||||
|
|||||||
45
allianceauth/optimer/form_widgets.py
Normal file
45
allianceauth/optimer/form_widgets.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"""
|
||||||
|
Form Widgets
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class DataListWidget(forms.TextInput):
|
||||||
|
"""
|
||||||
|
DataListWidget
|
||||||
|
|
||||||
|
Draws an HTML5 datalist form field
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, data_list, name, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._list = data_list
|
||||||
|
self.attrs.update({"list": "list__%s" % self._name})
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
|
"""
|
||||||
|
Render the DataList
|
||||||
|
:param name:
|
||||||
|
:type name:
|
||||||
|
:param value:
|
||||||
|
:type value:
|
||||||
|
:param attrs:
|
||||||
|
:type attrs:
|
||||||
|
:param renderer:
|
||||||
|
:type renderer:
|
||||||
|
:return:
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
|
|
||||||
|
text_html = super().render(name, value, attrs=attrs)
|
||||||
|
data_list = '<datalist id="list__%s">' % self._name
|
||||||
|
|
||||||
|
for item in self._list:
|
||||||
|
data_list += '<option value="%s">' % item
|
||||||
|
|
||||||
|
data_list += "</datalist>"
|
||||||
|
|
||||||
|
return text_html + data_list
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Generated by Django 3.2.8 on 2021-10-26 16:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("optimer", "0004_on_delete"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="OpTimerType",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("type", models.CharField(default="", max_length=254)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"ordering": ["type"],
|
||||||
|
"default_permissions": (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="optimer",
|
||||||
|
options={"default_permissions": (), "ordering": ["start"]},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="optimer",
|
||||||
|
name="description",
|
||||||
|
field=models.TextField(blank=True, default=""),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="optimer",
|
||||||
|
name="type",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="optimer.optimertype",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -6,9 +6,25 @@ from django.utils import timezone
|
|||||||
from allianceauth.eveonline.models import EveCharacter
|
from allianceauth.eveonline.models import EveCharacter
|
||||||
|
|
||||||
|
|
||||||
|
class OpTimerType(models.Model):
|
||||||
|
"""
|
||||||
|
Optimer Type
|
||||||
|
"""
|
||||||
|
|
||||||
|
type = models.CharField(max_length=254, default="")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.type
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['type']
|
||||||
|
default_permissions = ()
|
||||||
|
|
||||||
|
|
||||||
class OpTimer(models.Model):
|
class OpTimer(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['start']
|
ordering = ['start']
|
||||||
|
default_permissions = ()
|
||||||
|
|
||||||
doctrine = models.CharField(max_length=254, default="")
|
doctrine = models.CharField(max_length=254, default="")
|
||||||
system = models.CharField(max_length=254, default="")
|
system = models.CharField(max_length=254, default="")
|
||||||
@@ -17,7 +33,10 @@ class OpTimer(models.Model):
|
|||||||
operation_name = models.CharField(max_length=254, default="")
|
operation_name = models.CharField(max_length=254, default="")
|
||||||
fc = models.CharField(max_length=254, default="")
|
fc = models.CharField(max_length=254, default="")
|
||||||
post_time = models.DateTimeField(default=timezone.now)
|
post_time = models.DateTimeField(default=timezone.now)
|
||||||
eve_character = models.ForeignKey(EveCharacter, null=True, on_delete=models.SET_NULL)
|
eve_character = models.ForeignKey(EveCharacter, null=True,
|
||||||
|
on_delete=models.SET_NULL)
|
||||||
|
description = models.TextField(blank=True, default="")
|
||||||
|
type = models.ForeignKey(OpTimerType, null=True, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.operation_name
|
return self.operation_name
|
||||||
|
|||||||
@@ -6,37 +6,48 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center col-lg-3">{% translate "Operation Name" %}</th>
|
<th>{% translate "Operation Name" %}</th>
|
||||||
<th class="text-center col lg-2">{% translate "Doctrine" %}</th>
|
<th>{% translate "Description" %}</th>
|
||||||
<th class="text-center col-lg-1">{% translate "Form Up System" %}</th>
|
<th>{% translate "Doctrine" %}</th>
|
||||||
<th class="text-center col-lg-1">{% translate "Start Time" %}</th>
|
<th>{% translate "Form Up System" %}</th>
|
||||||
<th class="text-center col-lg-1">{% translate "Local Time" %}</th>
|
<th>{% translate "Start Time" %}</th>
|
||||||
<th class="text-center col-lg-1">{% translate "Duration" %}</th>
|
<th>{% translate "Local Time" %}</th>
|
||||||
<th class="text-center col-lg-1">{% translate "FC" %}</th>
|
<th>{% translate "Duration" %}</th>
|
||||||
|
<th>{% translate "FC" %}</th>
|
||||||
{% if perms.auth.optimer_management %}
|
{% if perms.auth.optimer_management %}
|
||||||
<th class="text-center col-lg-1">{% translate "Creator" %}</th>
|
{# <th>{% translate "Creator" %}</th>#}
|
||||||
<th class="text-center col-lg-2">{% translate "Action" %}</th>
|
<th class="text-right" style="width: 150px;">{% translate "Action" %}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{% for ops in timers %}
|
{% for ops in timers %}
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">{{ ops.operation_name }}</td>
|
<td>
|
||||||
<td class="text-center">{{ ops.doctrine }}</td>
|
{{ ops.operation_name }}
|
||||||
<td class="text-center">
|
{% if ops.type %}
|
||||||
|
<br>({{ ops.type }})
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{ ops.description }}</td>
|
||||||
|
<td>{{ ops.doctrine }}</td>
|
||||||
|
<td>
|
||||||
<a href="{{ ops.system|dotlan_solar_system_url }}">{{ ops.system }}</a>
|
<a href="{{ ops.system|dotlan_solar_system_url }}">{{ ops.system }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center" nowrap>{{ ops.start | date:"Y-m-d H:i" }}</td>
|
<td nowrap>{{ ops.start | date:"Y-m-d H:i" }}</td>
|
||||||
<td class="text-center" nowrap><div id="localtime{{ ops.id }}"></div><div id="countdown{{ ops.id }}"></div></td>
|
<td nowrap><div id="localtime{{ ops.id }}"></div><div id="countdown{{ ops.id }}"></div></td>
|
||||||
<td class="text-center">{{ ops.duration }}</td>
|
<td>{{ ops.duration }}</td>
|
||||||
<td class="text-center">{{ ops.fc }}</td>
|
<td>{{ ops.fc }}</td>
|
||||||
{% if perms.auth.optimer_management %}
|
{% if perms.auth.optimer_management %}
|
||||||
<td class="text-center">{{ ops.eve_character }}</td>
|
{# <td>{{ ops.eve_character }}</td>#}
|
||||||
<td class="text-center">
|
<td class="text-right">
|
||||||
<a href="{% url 'optimer:remove' ops.id %}" class="btn btn-danger">
|
<a href="{% url 'optimer:remove' ops.id %}" class="btn btn-danger">
|
||||||
<span class="glyphicon glyphicon-remove"></span>
|
<span class="glyphicon glyphicon-remove"></span>
|
||||||
</a><a href="{% url 'optimer:edit' ops.id %}" class="btn btn-info"><span class="glyphicon glyphicon-pencil"></span></a>
|
</a>
|
||||||
|
|
||||||
|
<a href="{% url 'optimer:edit' ops.id %}" class="btn btn-info">
|
||||||
|
<span class="glyphicon glyphicon-pencil"></span>
|
||||||
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -24,14 +24,14 @@
|
|||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4><b>{% translate "Next Timers" %}</b></h4>
|
<h4><b>{% translate "Next Fleet Operations" %}</b></h4>
|
||||||
{% if future_timers %}
|
{% if future_timers %}
|
||||||
{% include "optimer/fleetoptable.html" with timers=future_timers %}
|
{% include "optimer/fleetoptable.html" with timers=future_timers %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="alert alert-warning text-center">{% translate "No upcoming timers." %}</div>
|
<div class="alert alert-warning text-center">{% translate "No upcoming timers." %}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h4><b>{% translate "Past Timers" %}</b></h4>
|
<h4><b>{% translate "Past Fleet Operations" %}</b></h4>
|
||||||
{% if past_timers %}
|
{% if past_timers %}
|
||||||
{% include "optimer/fleetoptable.html" with timers=past_timers %}
|
{% include "optimer/fleetoptable.html" with timers=past_timers %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from django.utils import timezone
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .form import OpForm
|
from .form import OpForm
|
||||||
|
|
||||||
from .models import OpTimer
|
from .models import OpTimer, OpTimerType
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
|
|||||||
@permission_required('auth.optimer_view')
|
@permission_required('auth.optimer_view')
|
||||||
def optimer_view(request):
|
def optimer_view(request):
|
||||||
logger.debug("optimer_view called by user %s" % request.user)
|
logger.debug("optimer_view called by user %s" % request.user)
|
||||||
base_query = OpTimer.objects.select_related('eve_character')
|
base_query = OpTimer.objects.select_related('eve_character', 'type')
|
||||||
render_items = {'optimer': base_query.all(),
|
render_items = {'optimer': base_query.all(),
|
||||||
'future_timers': base_query.filter(
|
'future_timers': base_query.filter(
|
||||||
start__gte=timezone.now()),
|
start__gte=timezone.now()),
|
||||||
@@ -33,9 +33,21 @@ def optimer_view(request):
|
|||||||
def add_optimer_view(request):
|
def add_optimer_view(request):
|
||||||
logger.debug("add_optimer_view called by user %s" % request.user)
|
logger.debug("add_optimer_view called by user %s" % request.user)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = OpForm(request.POST)
|
form = OpForm(request.POST, data_list=OpTimerType.objects.all())
|
||||||
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
|
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
optimer_type = None
|
||||||
|
|
||||||
|
if form.cleaned_data['type'] != '':
|
||||||
|
try:
|
||||||
|
optimer_type = OpTimerType.objects.get(
|
||||||
|
type__iexact=form.cleaned_data['type']
|
||||||
|
)
|
||||||
|
except OpTimerType.DoesNotExist:
|
||||||
|
optimer_type = OpTimerType.objects.create(
|
||||||
|
type=form.cleaned_data['type']
|
||||||
|
)
|
||||||
|
|
||||||
# Get Current Time
|
# Get Current Time
|
||||||
post_time = timezone.now()
|
post_time = timezone.now()
|
||||||
# Get character
|
# Get character
|
||||||
@@ -50,13 +62,15 @@ def add_optimer_view(request):
|
|||||||
op.fc = form.cleaned_data['fc']
|
op.fc = form.cleaned_data['fc']
|
||||||
op.create_time = post_time
|
op.create_time = post_time
|
||||||
op.eve_character = character
|
op.eve_character = character
|
||||||
|
op.type = optimer_type
|
||||||
|
op.description = form.cleaned_data['description']
|
||||||
op.save()
|
op.save()
|
||||||
logger.info(f"User {request.user} created op timer with name {op.operation_name}")
|
logger.info(f"User {request.user} created op timer with name {op.operation_name}")
|
||||||
messages.success(request, _('Created operation timer for %(opname)s.') % {"opname": op.operation_name})
|
messages.success(request, _('Created operation timer for %(opname)s.') % {"opname": op.operation_name})
|
||||||
return redirect("optimer:view")
|
return redirect("optimer:view")
|
||||||
else:
|
else:
|
||||||
logger.debug("Returning new opForm")
|
logger.debug("Returning new opForm")
|
||||||
form = OpForm()
|
form = OpForm(data_list=OpTimerType.objects.all())
|
||||||
|
|
||||||
render_items = {'form': form}
|
render_items = {'form': form}
|
||||||
|
|
||||||
@@ -80,10 +94,23 @@ def edit_optimer(request, optimer_id):
|
|||||||
logger.debug(f"edit_optimer called by user {request.user} for optimer id {optimer_id}")
|
logger.debug(f"edit_optimer called by user {request.user} for optimer id {optimer_id}")
|
||||||
op = get_object_or_404(OpTimer, id=optimer_id)
|
op = get_object_or_404(OpTimer, id=optimer_id)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = OpForm(request.POST)
|
form = OpForm(request.POST, data_list=OpTimerType.objects.all())
|
||||||
logger.debug("Received POST request containing update optimer form, is valid: %s" % form.is_valid())
|
logger.debug("Received POST request containing update optimer form, is valid: %s" % form.is_valid())
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
character = request.user.profile.main_character
|
character = request.user.profile.main_character
|
||||||
|
|
||||||
|
optimer_type = None
|
||||||
|
|
||||||
|
if form.cleaned_data['type'] != '':
|
||||||
|
try:
|
||||||
|
optimer_type = OpTimerType.objects.get(
|
||||||
|
type__iexact=form.cleaned_data['type']
|
||||||
|
)
|
||||||
|
except OpTimerType.DoesNotExist:
|
||||||
|
optimer_type = OpTimerType.objects.create(
|
||||||
|
type=form.cleaned_data['type']
|
||||||
|
)
|
||||||
|
|
||||||
op.doctrine = form.cleaned_data['doctrine']
|
op.doctrine = form.cleaned_data['doctrine']
|
||||||
op.system = form.cleaned_data['system']
|
op.system = form.cleaned_data['system']
|
||||||
op.start = form.cleaned_data['start']
|
op.start = form.cleaned_data['start']
|
||||||
@@ -91,6 +118,8 @@ def edit_optimer(request, optimer_id):
|
|||||||
op.operation_name = form.cleaned_data['operation_name']
|
op.operation_name = form.cleaned_data['operation_name']
|
||||||
op.fc = form.cleaned_data['fc']
|
op.fc = form.cleaned_data['fc']
|
||||||
op.eve_character = character
|
op.eve_character = character
|
||||||
|
op.type = optimer_type
|
||||||
|
op.description = form.cleaned_data['description']
|
||||||
logger.info(f"User {request.user} updating optimer id {optimer_id} ")
|
logger.info(f"User {request.user} updating optimer id {optimer_id} ")
|
||||||
op.save()
|
op.save()
|
||||||
messages.success(request, _('Saved changes to operation timer for %(opname)s.') % {"opname": op.operation_name})
|
messages.success(request, _('Saved changes to operation timer for %(opname)s.') % {"opname": op.operation_name})
|
||||||
@@ -103,6 +132,8 @@ def edit_optimer(request, optimer_id):
|
|||||||
'duration': op.duration,
|
'duration': op.duration,
|
||||||
'operation_name': op.operation_name,
|
'operation_name': op.operation_name,
|
||||||
'fc': op.fc,
|
'fc': op.fc,
|
||||||
|
'description': op.description,
|
||||||
|
'type': op.type
|
||||||
}
|
}
|
||||||
form = OpForm(initial=data)
|
form = OpForm(initial=data, data_list=OpTimerType.objects.all())
|
||||||
return render(request, 'optimer/update.html', context={'form': form})
|
return render(request, 'optimer/update.html', context={'form': form})
|
||||||
|
|||||||
@@ -73,6 +73,8 @@
|
|||||||
],
|
],
|
||||||
bootstrap: true
|
bootstrap: true
|
||||||
},
|
},
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0,
|
||||||
drawCallback: function ( settings ) {
|
drawCallback: function ( settings ) {
|
||||||
let api = this.api();
|
let api = this.api();
|
||||||
let rows = api.rows( {page:'current'} ).nodes();
|
let rows = api.rows( {page:'current'} ).nodes();
|
||||||
|
|||||||
@@ -106,8 +106,10 @@
|
|||||||
idx: 1
|
idx: 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
bootstrap: true
|
bootstrap: true,
|
||||||
},
|
},
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0,
|
||||||
drawCallback: function ( settings ) {
|
drawCallback: function ( settings ) {
|
||||||
let api = this.api();
|
let api = this.api();
|
||||||
let rows = api.rows( {page:'current'} ).nodes();
|
let rows = api.rows( {page:'current'} ).nodes();
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
|||||||
|
|
||||||
# Bootstrap messaging css workaround
|
# Bootstrap messaging css workaround
|
||||||
MESSAGE_TAGS = {
|
MESSAGE_TAGS = {
|
||||||
messages.ERROR: 'danger'
|
messages.ERROR: 'danger error'
|
||||||
}
|
}
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
|
from typing import Set, Iterable
|
||||||
|
|
||||||
|
|
||||||
class DiscordRoles:
|
class DiscordRoles:
|
||||||
@@ -39,7 +40,7 @@ class DiscordRoles:
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._roles.keys())
|
return len(self._roles.keys())
|
||||||
|
|
||||||
def has_roles(self, role_ids: set) -> bool:
|
def has_roles(self, role_ids: Set[int]) -> bool:
|
||||||
"""returns true if this objects contains all roles defined by given role_ids
|
"""returns true if this objects contains all roles defined by given role_ids
|
||||||
incl. managed roles
|
incl. managed roles
|
||||||
"""
|
"""
|
||||||
@@ -47,13 +48,22 @@ class DiscordRoles:
|
|||||||
all_role_ids = self._roles.keys()
|
all_role_ids = self._roles.keys()
|
||||||
return role_ids.issubset(all_role_ids)
|
return role_ids.issubset(all_role_ids)
|
||||||
|
|
||||||
def ids(self) -> set:
|
def ids(self) -> Set[int]:
|
||||||
"""return a set of all role IDs"""
|
"""return a set of all role IDs"""
|
||||||
return set(self._roles.keys())
|
return set(self._roles.keys())
|
||||||
|
|
||||||
def subset(self, role_ids: set = None, managed_only: bool = False) -> object:
|
def subset(
|
||||||
"""returns a new object containing the subset of roles as defined
|
self,
|
||||||
by given role IDs and/or including managed roles only
|
role_ids: Iterable[int] = None,
|
||||||
|
managed_only: bool = False,
|
||||||
|
role_names: Iterable[str] = None
|
||||||
|
) -> "DiscordRoles":
|
||||||
|
"""returns a new object containing the subset of roles
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- role_ids: role ids must be in the provided list
|
||||||
|
- managed_only: roles must be managed
|
||||||
|
- role_names: role names must match provided list (not case sensitive)
|
||||||
"""
|
"""
|
||||||
if role_ids is not None:
|
if role_ids is not None:
|
||||||
role_ids = {int(id) for id in role_ids}
|
role_ids = {int(id) for id in role_ids}
|
||||||
@@ -74,15 +84,21 @@ class DiscordRoles:
|
|||||||
if role_id in role_ids and role['managed']
|
if role_id in role_ids and role['managed']
|
||||||
])
|
])
|
||||||
|
|
||||||
else:
|
elif role_ids is None and managed_only is False and role_names is not None:
|
||||||
return copy(self)
|
role_names = {self.sanitize_role_name(name).lower() for name in role_names}
|
||||||
|
return type(self)([
|
||||||
|
role for role in self._roles.values()
|
||||||
|
if role["name"].lower() in role_names
|
||||||
|
])
|
||||||
|
|
||||||
def union(self, other: object) -> object:
|
return copy(self)
|
||||||
|
|
||||||
|
def union(self, other: object) -> "DiscordRoles":
|
||||||
"""returns a new roles object that is the union of this roles object
|
"""returns a new roles object that is the union of this roles object
|
||||||
with other"""
|
with other"""
|
||||||
return type(self)(list(self) + list(other))
|
return type(self)(list(self) + list(other))
|
||||||
|
|
||||||
def difference(self, other: object) -> object:
|
def difference(self, other: object) -> "DiscordRoles":
|
||||||
"""returns a new roles object that only contains the roles
|
"""returns a new roles object that only contains the roles
|
||||||
that exist in the current objects, but not in other
|
that exist in the current objects, but not in other
|
||||||
"""
|
"""
|
||||||
@@ -94,11 +110,10 @@ class DiscordRoles:
|
|||||||
role_name = self.sanitize_role_name(role_name)
|
role_name = self.sanitize_role_name(role_name)
|
||||||
if role_name in self._roles_by_name:
|
if role_name in self._roles_by_name:
|
||||||
return self._roles_by_name[role_name]
|
return self._roles_by_name[role_name]
|
||||||
else:
|
return dict()
|
||||||
return dict()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_matched_roles(cls, matched_roles: list) -> None:
|
def create_from_matched_roles(cls, matched_roles: list) -> "DiscordRoles":
|
||||||
"""returns a new object created from the given list of matches roles
|
"""returns a new object created from the given list of matches roles
|
||||||
|
|
||||||
matches_roles must be a list of tuples in the form: (role, created)
|
matches_roles must be a list of tuples in the form: (role, created)
|
||||||
@@ -107,7 +122,7 @@ class DiscordRoles:
|
|||||||
return cls(raw_roles)
|
return cls(raw_roles)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _assert_valid_role(role: dict):
|
def _assert_valid_role(role: dict) -> None:
|
||||||
if not isinstance(role, dict):
|
if not isinstance(role, dict):
|
||||||
raise TypeError('Roles must be of type dict: %s' % role)
|
raise TypeError('Roles must be of type dict: %s' % role)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ TEST_BOT_TOKEN = 'abcdefhijlkmnopqastzvwxyz1234567890ABCDEFGHOJKLMNOPQRSTUVWXY'
|
|||||||
TEST_ROLE_ID = 654321012345678912
|
TEST_ROLE_ID = 654321012345678912
|
||||||
|
|
||||||
|
|
||||||
def create_role(id: int, name: str, managed=False):
|
def create_role(id: int, name: str, managed=False) -> dict:
|
||||||
return {
|
return {
|
||||||
'id': int(id),
|
'id': int(id),
|
||||||
'name': str(name),
|
'name': str(name),
|
||||||
@@ -21,8 +21,10 @@ def create_matched_role(role, created=False) -> tuple:
|
|||||||
ROLE_ALPHA = create_role(1, 'alpha')
|
ROLE_ALPHA = create_role(1, 'alpha')
|
||||||
ROLE_BRAVO = create_role(2, 'bravo')
|
ROLE_BRAVO = create_role(2, 'bravo')
|
||||||
ROLE_CHARLIE = create_role(3, 'charlie')
|
ROLE_CHARLIE = create_role(3, 'charlie')
|
||||||
|
ROLE_CHARLIE_2 = create_role(4, 'Charlie') # Discord roles are case sensitive
|
||||||
ROLE_MIKE = create_role(13, 'mike', True)
|
ROLE_MIKE = create_role(13, 'mike', True)
|
||||||
|
|
||||||
|
|
||||||
ALL_ROLES = [ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE]
|
ALL_ROLES = [ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from . import ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE, ALL_ROLES, create_role
|
from . import (
|
||||||
|
ROLE_ALPHA,
|
||||||
|
ROLE_BRAVO,
|
||||||
|
ROLE_CHARLIE,
|
||||||
|
ROLE_CHARLIE_2,
|
||||||
|
ROLE_MIKE,
|
||||||
|
ALL_ROLES,
|
||||||
|
create_role
|
||||||
|
)
|
||||||
from .. import DiscordRoles
|
from .. import DiscordRoles
|
||||||
|
|
||||||
|
|
||||||
@@ -143,6 +151,16 @@ class TestSubset(TestCase):
|
|||||||
expected = {1, 2, 3, 13}
|
expected = {1, 2, 3, 13}
|
||||||
self.assertSetEqual(roles.ids(), expected)
|
self.assertSetEqual(roles.ids(), expected)
|
||||||
|
|
||||||
|
def test_should_return_role_names_only(self):
|
||||||
|
# given
|
||||||
|
all_roles = DiscordRoles([
|
||||||
|
ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE, ROLE_CHARLIE_2
|
||||||
|
])
|
||||||
|
# when
|
||||||
|
roles = all_roles.subset(role_names={"bravo", "charlie"})
|
||||||
|
# then
|
||||||
|
self.assertSetEqual(roles.ids(), {2, 3, 4})
|
||||||
|
|
||||||
|
|
||||||
class TestHasRoles(TestCase):
|
class TestHasRoles(TestCase):
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ from django.contrib.auth.models import User
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy
|
from django.utils.translation import gettext_lazy
|
||||||
|
|
||||||
|
from allianceauth.groupmanagement.models import ReservedGroupName
|
||||||
from allianceauth.notifications import notify
|
from allianceauth.notifications import notify
|
||||||
|
|
||||||
from . import __title__
|
from . import __title__
|
||||||
from .app_settings import DISCORD_GUILD_ID
|
from .app_settings import DISCORD_GUILD_ID
|
||||||
from .discord_client import DiscordApiBackoff, DiscordRoles
|
from .discord_client import DiscordApiBackoff, DiscordClient, DiscordRoles
|
||||||
from .discord_client.helpers import match_or_create_roles_from_names
|
from .discord_client.helpers import match_or_create_roles_from_names
|
||||||
from .managers import DiscordUserManager
|
from .managers import DiscordUserManager
|
||||||
from .utils import LoggerAddTag
|
from .utils import LoggerAddTag
|
||||||
@@ -109,11 +110,16 @@ class DiscordUser(models.Model):
|
|||||||
- False on error or raises exception
|
- False on error or raises exception
|
||||||
"""
|
"""
|
||||||
client = DiscordUser.objects._bot_client()
|
client = DiscordUser.objects._bot_client()
|
||||||
|
member_roles = self._determine_member_roles(client)
|
||||||
|
if member_roles is None:
|
||||||
|
return None
|
||||||
|
return self._update_roles_if_needed(client, state_name, member_roles)
|
||||||
|
|
||||||
|
def _determine_member_roles(self, client: DiscordClient) -> DiscordRoles:
|
||||||
|
"""Determine the roles of the current member / user."""
|
||||||
member_info = client.guild_member(guild_id=DISCORD_GUILD_ID, user_id=self.uid)
|
member_info = client.guild_member(guild_id=DISCORD_GUILD_ID, user_id=self.uid)
|
||||||
if member_info is None:
|
if member_info is None:
|
||||||
# User is no longer a member
|
return None # User is no longer a member
|
||||||
return None
|
|
||||||
|
|
||||||
guild_roles = DiscordRoles(client.guild_roles(guild_id=DISCORD_GUILD_ID))
|
guild_roles = DiscordRoles(client.guild_roles(guild_id=DISCORD_GUILD_ID))
|
||||||
logger.debug('Current guild roles: %s', guild_roles.ids())
|
logger.debug('Current guild roles: %s', guild_roles.ids())
|
||||||
if 'roles' in member_info:
|
if 'roles' in member_info:
|
||||||
@@ -128,10 +134,13 @@ class DiscordUser(models.Model):
|
|||||||
set(member_info['roles']).difference(guild_roles.ids())
|
set(member_info['roles']).difference(guild_roles.ids())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
member_roles = guild_roles.subset(member_info['roles'])
|
return guild_roles.subset(member_info['roles'])
|
||||||
else:
|
raise RuntimeError('member_info from %s is not valid' % self.user)
|
||||||
raise RuntimeError('member_info from %s is not valid' % self.user)
|
|
||||||
|
|
||||||
|
def _update_roles_if_needed(
|
||||||
|
self, client: DiscordClient, state_name: str, member_roles: DiscordRoles
|
||||||
|
) -> bool:
|
||||||
|
"""Update the roles of this member/user if needed."""
|
||||||
requested_roles = match_or_create_roles_from_names(
|
requested_roles = match_or_create_roles_from_names(
|
||||||
client=client,
|
client=client,
|
||||||
guild_id=DISCORD_GUILD_ID,
|
guild_id=DISCORD_GUILD_ID,
|
||||||
@@ -143,10 +152,13 @@ class DiscordUser(models.Model):
|
|||||||
'Requested roles for user %s: %s', self.user, requested_roles.ids()
|
'Requested roles for user %s: %s', self.user, requested_roles.ids()
|
||||||
)
|
)
|
||||||
logger.debug('Current roles user %s: %s', self.user, member_roles.ids())
|
logger.debug('Current roles user %s: %s', self.user, member_roles.ids())
|
||||||
|
reserved_role_names = ReservedGroupName.objects.values_list("name", flat=True)
|
||||||
|
member_roles_reserved = member_roles.subset(role_names=reserved_role_names)
|
||||||
member_roles_managed = member_roles.subset(managed_only=True)
|
member_roles_managed = member_roles.subset(managed_only=True)
|
||||||
if requested_roles != member_roles.difference(member_roles_managed):
|
member_roles_persistent = member_roles_managed.union(member_roles_reserved)
|
||||||
|
if requested_roles != member_roles.difference(member_roles_persistent):
|
||||||
logger.debug('Need to update roles for user %s', self.user)
|
logger.debug('Need to update roles for user %s', self.user)
|
||||||
new_roles = requested_roles.union(member_roles_managed)
|
new_roles = requested_roles.union(member_roles_persistent)
|
||||||
success = client.modify_guild_member(
|
success = client.modify_guild_member(
|
||||||
guild_id=DISCORD_GUILD_ID,
|
guild_id=DISCORD_GUILD_ID,
|
||||||
user_id=self.uid,
|
user_id=self.uid,
|
||||||
@@ -157,10 +169,8 @@ class DiscordUser(models.Model):
|
|||||||
else:
|
else:
|
||||||
logger.warning('Failed to update roles for %s', self.user)
|
logger.warning('Failed to update roles for %s', self.user)
|
||||||
return success
|
return success
|
||||||
|
logger.info('No need to update roles for user %s', self.user)
|
||||||
else:
|
return True
|
||||||
logger.info('No need to update roles for user %s', self.user)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def update_username(self) -> bool:
|
def update_username(self) -> bool:
|
||||||
"""Updates the username incl. the discriminator
|
"""Updates the username incl. the discriminator
|
||||||
@@ -171,7 +181,6 @@ class DiscordUser(models.Model):
|
|||||||
- None if user is no longer a member of the Discord server
|
- None if user is no longer a member of the Discord server
|
||||||
- False on error or raises exception
|
- False on error or raises exception
|
||||||
"""
|
"""
|
||||||
|
|
||||||
client = DiscordUser.objects._bot_client()
|
client = DiscordUser.objects._bot_client()
|
||||||
user_info = client.guild_member(guild_id=DISCORD_GUILD_ID, user_id=self.uid)
|
user_info = client.guild_member(guild_id=DISCORD_GUILD_ID, user_id=self.uid)
|
||||||
if user_info is None:
|
if user_info is None:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from ..discord_client.tests import ( # noqa
|
|||||||
ROLE_ALPHA,
|
ROLE_ALPHA,
|
||||||
ROLE_BRAVO,
|
ROLE_BRAVO,
|
||||||
ROLE_CHARLIE,
|
ROLE_CHARLIE,
|
||||||
|
ROLE_CHARLIE_2,
|
||||||
ROLE_MIKE,
|
ROLE_MIKE,
|
||||||
ALL_ROLES,
|
ALL_ROLES,
|
||||||
create_user_info
|
create_user_info
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from requests.exceptions import HTTPError
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
|
from allianceauth.groupmanagement.models import ReservedGroupName
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
TEST_USER_NAME,
|
TEST_USER_NAME,
|
||||||
@@ -15,7 +16,8 @@ from . import (
|
|||||||
ROLE_ALPHA,
|
ROLE_ALPHA,
|
||||||
ROLE_BRAVO,
|
ROLE_BRAVO,
|
||||||
ROLE_CHARLIE,
|
ROLE_CHARLIE,
|
||||||
ROLE_MIKE
|
ROLE_CHARLIE_2,
|
||||||
|
ROLE_MIKE,
|
||||||
)
|
)
|
||||||
from ..discord_client import DiscordClient, DiscordApiBackoff
|
from ..discord_client import DiscordClient, DiscordApiBackoff
|
||||||
from ..discord_client.tests import create_matched_role
|
from ..discord_client.tests import create_matched_role
|
||||||
@@ -294,25 +296,33 @@ class TestUpdateGroups(TestCase):
|
|||||||
args, kwargs = mock_DiscordClient.return_value.modify_guild_member.call_args
|
args, kwargs = mock_DiscordClient.return_value.modify_guild_member.call_args
|
||||||
self.assertEqual(set(kwargs['role_ids']), {1, 2})
|
self.assertEqual(set(kwargs['role_ids']), {1, 2})
|
||||||
|
|
||||||
def test_update_if_needed_and_preserve_managed_roles(
|
def test_should_update_and_preserve_managed_and_reserved_roles(
|
||||||
self,
|
self,
|
||||||
mock_user_group_names,
|
mock_user_group_names,
|
||||||
mock_DiscordClient
|
mock_DiscordClient
|
||||||
):
|
):
|
||||||
roles_current = [1, 13]
|
# given
|
||||||
|
roles_current = [1, 3, 4, 13]
|
||||||
mock_user_group_names.return_value = []
|
mock_user_group_names.return_value = []
|
||||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||||
.return_value = self.roles_requested
|
.return_value = self.roles_requested
|
||||||
mock_DiscordClient.return_value.guild_roles.return_value = self.guild_roles
|
mock_DiscordClient.return_value.guild_roles.return_value = [
|
||||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE, ROLE_CHARLIE_2
|
||||||
{'roles': roles_current}
|
]
|
||||||
|
mock_DiscordClient.return_value.guild_member.return_value = {
|
||||||
|
'roles': roles_current
|
||||||
|
}
|
||||||
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
||||||
|
ReservedGroupName.objects.create(
|
||||||
|
name="charlie", reason="dummy", created_by="xyz"
|
||||||
|
)
|
||||||
|
# when
|
||||||
result = self.discord_user.update_groups()
|
result = self.discord_user.update_groups()
|
||||||
|
# then
|
||||||
self.assertTrue(result)
|
self.assertTrue(result)
|
||||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||||
args, kwargs = mock_DiscordClient.return_value.modify_guild_member.call_args
|
args, kwargs = mock_DiscordClient.return_value.modify_guild_member.call_args
|
||||||
self.assertEqual(set(kwargs['role_ids']), {1, 2, 13})
|
self.assertEqual(set(kwargs['role_ids']), {1, 2, 3, 4, 13})
|
||||||
|
|
||||||
def test_dont_update_if_not_needed(
|
def test_dont_update_if_not_needed(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
from .models import AuthTS, Teamspeak3User, StateGroup
|
from .models import AuthTS, Teamspeak3User, StateGroup, TSgroup
|
||||||
from ...admin import ServicesUserAdmin
|
from ...admin import ServicesUserAdmin
|
||||||
|
from allianceauth.groupmanagement.models import ReservedGroupName
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Teamspeak3User)
|
@admin.register(Teamspeak3User)
|
||||||
@@ -25,6 +26,16 @@ class AuthTSgroupAdmin(admin.ModelAdmin):
|
|||||||
fields = ('auth_group', 'ts_group')
|
fields = ('auth_group', 'ts_group')
|
||||||
filter_horizontal = ('ts_group',)
|
filter_horizontal = ('ts_group',)
|
||||||
|
|
||||||
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||||
|
if db_field.name == 'auth_group':
|
||||||
|
kwargs['queryset'] = Group.objects.exclude(name__in=ReservedGroupName.objects.values_list('name', flat=True))
|
||||||
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
if db_field.name == 'ts_group':
|
||||||
|
kwargs['queryset'] = TSgroup.objects.exclude(ts_group_name__in=ReservedGroupName.objects.values_list('name', flat=True))
|
||||||
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
||||||
def _ts_group(self, obj):
|
def _ts_group(self, obj):
|
||||||
return [x for x in obj.ts_group.all().order_by('ts_group_id')]
|
return [x for x in obj.ts_group.all().order_by('ts_group_id')]
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from django.conf import settings
|
|||||||
|
|
||||||
from .util.ts3 import TS3Server, TeamspeakError
|
from .util.ts3 import TS3Server, TeamspeakError
|
||||||
from .models import TSgroup
|
from .models import TSgroup
|
||||||
|
from allianceauth.groupmanagement.models import ReservedGroupName
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -156,32 +157,25 @@ class Teamspeak3Manager:
|
|||||||
logger.info(f"Removed user id {uid} from group id {groupid} on TS3 server.")
|
logger.info(f"Removed user id {uid} from group id {groupid} on TS3 server.")
|
||||||
|
|
||||||
def _sync_ts_group_db(self):
|
def _sync_ts_group_db(self):
|
||||||
logger.debug("_sync_ts_group_db function called.")
|
|
||||||
try:
|
try:
|
||||||
remote_groups = self._group_list()
|
remote_groups = self._group_list()
|
||||||
local_groups = TSgroup.objects.all()
|
managed_groups = {g:int(remote_groups[g]) for g in remote_groups if g in set(remote_groups.keys()) - set(ReservedGroupName.objects.values_list('name', flat=True))}
|
||||||
logger.debug("Comparing remote groups to TSgroup objects: %s" % local_groups)
|
remove = TSgroup.objects.exclude(ts_group_id__in=managed_groups.values())
|
||||||
for key in remote_groups:
|
|
||||||
logger.debug(f"Typecasting remote_group value at position {key} to int: {remote_groups[key]}")
|
if remove:
|
||||||
remote_groups[key] = int(remote_groups[key])
|
logger.debug(f"Deleting {remove.count()} TSgroup models: not found on server, or reserved name.")
|
||||||
|
remove.delete()
|
||||||
|
|
||||||
|
add = {g:managed_groups[g] for g in managed_groups if managed_groups[g] in set(managed_groups.values()) - set(TSgroup.objects.values_list("ts_group_id", flat=True))}
|
||||||
|
if add:
|
||||||
|
logger.debug(f"Adding {len(add)} new TSgroup models.")
|
||||||
|
models = [TSgroup(ts_group_name=name, ts_group_id=add[name]) for name in add]
|
||||||
|
TSgroup.objects.bulk_create(models)
|
||||||
|
|
||||||
for group in local_groups:
|
|
||||||
logger.debug("Checking local group %s" % group)
|
|
||||||
if group.ts_group_id not in remote_groups.values():
|
|
||||||
logger.debug(
|
|
||||||
f"Local group id {group.ts_group_id} not found on server. Deleting model {group}")
|
|
||||||
TSgroup.objects.filter(ts_group_id=group.ts_group_id).delete()
|
|
||||||
for key in remote_groups:
|
|
||||||
g = TSgroup(ts_group_id=remote_groups[key], ts_group_name=key)
|
|
||||||
q = TSgroup.objects.filter(ts_group_id=g.ts_group_id)
|
|
||||||
if not q:
|
|
||||||
logger.debug("Local group does not exist for TS group {}. Creating TSgroup model {}".format(
|
|
||||||
remote_groups[key], g))
|
|
||||||
g.save()
|
|
||||||
except TeamspeakError as e:
|
except TeamspeakError as e:
|
||||||
logger.error("Error occured while syncing TS group db: %s" % str(e))
|
logger.error(f"Error occurred while syncing TS group db: {str(e)}")
|
||||||
except:
|
except Exception:
|
||||||
logger.exception("An unhandled exception has occured while syncing TS groups.")
|
logger.exception(f"An unhandled exception has occurred while syncing TS groups.")
|
||||||
|
|
||||||
def add_user(self, user, fmt_name):
|
def add_user(self, user, fmt_name):
|
||||||
username_clean = self.__santatize_username(fmt_name[:30])
|
username_clean = self.__santatize_username(fmt_name[:30])
|
||||||
@@ -240,7 +234,7 @@ class Teamspeak3Manager:
|
|||||||
logger.exception(f"Failed to delete user id {uid} from TS3 - received response {ret}")
|
logger.exception(f"Failed to delete user id {uid} from TS3 - received response {ret}")
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
logger.warn("User with id %s not found on TS3 server. Assuming succesful deletion." % uid)
|
logger.warning("User with id %s not found on TS3 server. Assuming succesful deletion." % uid)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_user_exists(self, uid):
|
def check_user_exists(self, uid):
|
||||||
@@ -270,7 +264,8 @@ class Teamspeak3Manager:
|
|||||||
addgroups.append(ts_groups[ts_group_key])
|
addgroups.append(ts_groups[ts_group_key])
|
||||||
for user_ts_group_key in user_ts_groups:
|
for user_ts_group_key in user_ts_groups:
|
||||||
if user_ts_groups[user_ts_group_key] not in ts_groups.values():
|
if user_ts_groups[user_ts_group_key] not in ts_groups.values():
|
||||||
remgroups.append(user_ts_groups[user_ts_group_key])
|
if not ReservedGroupName.objects.filter(name=user_ts_group_key).exists():
|
||||||
|
remgroups.append(user_ts_groups[user_ts_group_key])
|
||||||
|
|
||||||
for g in addgroups:
|
for g in addgroups:
|
||||||
logger.info(f"Adding Teamspeak user {userid} into group {g}")
|
logger.info(f"Adding Teamspeak user {userid} into group {g}")
|
||||||
|
|||||||
@@ -5,16 +5,18 @@ from django import urls
|
|||||||
from django.contrib.auth.models import User, Group, Permission
|
from django.contrib.auth.models import User, Group, Permission
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
|
from django.contrib.admin import AdminSite
|
||||||
|
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
from .auth_hooks import Teamspeak3Service
|
from .auth_hooks import Teamspeak3Service
|
||||||
from .models import Teamspeak3User, AuthTS, TSgroup, StateGroup
|
from .models import Teamspeak3User, AuthTS, TSgroup, StateGroup
|
||||||
from .tasks import Teamspeak3Tasks
|
from .tasks import Teamspeak3Tasks
|
||||||
from .signals import m2m_changed_authts_group, post_save_authts, post_delete_authts
|
from .signals import m2m_changed_authts_group, post_save_authts, post_delete_authts
|
||||||
|
from .admin import AuthTSgroupAdmin
|
||||||
|
|
||||||
from .manager import Teamspeak3Manager
|
from .manager import Teamspeak3Manager
|
||||||
from .util.ts3 import TeamspeakError
|
from .util.ts3 import TeamspeakError
|
||||||
from allianceauth.authentication.models import State
|
from allianceauth.groupmanagement.models import ReservedGroupName
|
||||||
|
|
||||||
MODULE_PATH = 'allianceauth.services.modules.teamspeak3'
|
MODULE_PATH = 'allianceauth.services.modules.teamspeak3'
|
||||||
DEFAULT_AUTH_GROUP = 'Member'
|
DEFAULT_AUTH_GROUP = 'Member'
|
||||||
@@ -315,6 +317,9 @@ class Teamspeak3SignalsTestCase(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class Teamspeak3ManagerTestCase(TestCase):
|
class Teamspeak3ManagerTestCase(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.reserved = ReservedGroupName.objects.create(name='reserved', reason='tests', created_by='Bob, praise be!')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def my_side_effect(*args, **kwargs):
|
def my_side_effect(*args, **kwargs):
|
||||||
@@ -334,8 +339,135 @@ class Teamspeak3ManagerTestCase(TestCase):
|
|||||||
manager._server = server
|
manager._server = server
|
||||||
|
|
||||||
# create test data
|
# create test data
|
||||||
user = User.objects.create_user("dummy")
|
user = AuthUtils.create_user("dummy")
|
||||||
user.profile.state = State.objects.filter(name="Member").first()
|
AuthUtils.assign_state(user, AuthUtils.get_member_state())
|
||||||
|
|
||||||
# perform test
|
# perform test
|
||||||
manager.add_user(user, "Dummy User")
|
manager.add_user(user, "Dummy User")
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_get_userid')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_user_group_list')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_add_user_to_group')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_remove_user_from_group')
|
||||||
|
def test_update_groups_add(self, remove, add, groups, userid):
|
||||||
|
"""Add to one group"""
|
||||||
|
userid.return_value = 1
|
||||||
|
groups.return_value = {'test': 1}
|
||||||
|
|
||||||
|
Teamspeak3Manager().update_groups(1, {'test': 1, 'dummy': 2})
|
||||||
|
self.assertEqual(add.call_count, 1)
|
||||||
|
self.assertEqual(remove.call_count, 0)
|
||||||
|
self.assertEqual(add.call_args[0][1], 2)
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_get_userid')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_user_group_list')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_add_user_to_group')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_remove_user_from_group')
|
||||||
|
def test_update_groups_remove(self, remove, add, groups, userid):
|
||||||
|
"""Remove from one group"""
|
||||||
|
userid.return_value = 1
|
||||||
|
groups.return_value = {'test': '1', 'dummy': '2'}
|
||||||
|
|
||||||
|
Teamspeak3Manager().update_groups(1, {'test': 1})
|
||||||
|
self.assertEqual(add.call_count, 0)
|
||||||
|
self.assertEqual(remove.call_count, 1)
|
||||||
|
self.assertEqual(remove.call_args[0][1], 2)
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_get_userid')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_user_group_list')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_add_user_to_group')
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_remove_user_from_group')
|
||||||
|
def test_update_groups_remove_reserved(self, remove, add, groups, userid):
|
||||||
|
"""Remove from one group, but do not touch reserved group"""
|
||||||
|
userid.return_value = 1
|
||||||
|
groups.return_value = {'test': 1, 'dummy': 2, self.reserved.name: 3}
|
||||||
|
|
||||||
|
Teamspeak3Manager().update_groups(1, {'test': 1})
|
||||||
|
self.assertEqual(add.call_count, 0)
|
||||||
|
self.assertEqual(remove.call_count, 1)
|
||||||
|
self.assertEqual(remove.call_args[0][1], 2)
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_group_list')
|
||||||
|
def test_sync_group_db_create(self, group_list):
|
||||||
|
"""Populate the list of all TSgroups"""
|
||||||
|
group_list.return_value = {'allowed':'1', 'also allowed':'2'}
|
||||||
|
Teamspeak3Manager()._sync_ts_group_db()
|
||||||
|
self.assertEqual(TSgroup.objects.all().count(), 2)
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_group_list')
|
||||||
|
def test_sync_group_db_delete(self, group_list):
|
||||||
|
"""Populate the list of all TSgroups, and delete one which no longer exists"""
|
||||||
|
TSgroup.objects.create(ts_group_name='deleted', ts_group_id=3)
|
||||||
|
group_list.return_value = {'allowed': '1'}
|
||||||
|
Teamspeak3Manager()._sync_ts_group_db()
|
||||||
|
self.assertEqual(TSgroup.objects.all().count(), 1)
|
||||||
|
self.assertFalse(TSgroup.objects.filter(ts_group_name='deleted').exists())
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_group_list')
|
||||||
|
def test_sync_group_db_dont_create_reserved(self, group_list):
|
||||||
|
"""Populate the list of all TSgroups, ignoring a reserved group name"""
|
||||||
|
group_list.return_value = {'allowed': '1', 'reserved': '4'}
|
||||||
|
Teamspeak3Manager()._sync_ts_group_db()
|
||||||
|
self.assertEqual(TSgroup.objects.all().count(), 1)
|
||||||
|
self.assertFalse(TSgroup.objects.filter(ts_group_name='reserved').exists())
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_group_list')
|
||||||
|
def test_sync_group_db_delete_reserved(self, group_list):
|
||||||
|
"""Populate the list of all TSgroups, deleting the TSgroup model for one which has become reserved"""
|
||||||
|
TSgroup.objects.create(ts_group_name='reserved', ts_group_id=4)
|
||||||
|
group_list.return_value = {'allowed': '1', 'reserved': '4'}
|
||||||
|
Teamspeak3Manager()._sync_ts_group_db()
|
||||||
|
self.assertEqual(TSgroup.objects.all().count(), 1)
|
||||||
|
self.assertFalse(TSgroup.objects.filter(ts_group_name='reserved').exists())
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_group_list')
|
||||||
|
def test_sync_group_db_partial_addition(self, group_list):
|
||||||
|
"""Some TSgroups already exist in database, add new ones"""
|
||||||
|
TSgroup.objects.create(ts_group_name='allowed', ts_group_id=1)
|
||||||
|
group_list.return_value = {'allowed': '1', 'also allowed': '2'}
|
||||||
|
Teamspeak3Manager()._sync_ts_group_db()
|
||||||
|
self.assertEqual(TSgroup.objects.all().count(), 2)
|
||||||
|
|
||||||
|
@mock.patch.object(Teamspeak3Manager, '_group_list')
|
||||||
|
def test_sync_group_db_partial_removal(self, group_list):
|
||||||
|
"""One TSgroup has been deleted on server, so remove its model"""
|
||||||
|
TSgroup.objects.create(ts_group_name='allowed', ts_group_id=1)
|
||||||
|
TSgroup.objects.create(ts_group_name='also allowed', ts_group_id=2)
|
||||||
|
group_list.return_value = {'allowed': '1'}
|
||||||
|
Teamspeak3Manager()._sync_ts_group_db()
|
||||||
|
self.assertEqual(TSgroup.objects.all().count(), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class MockRequest:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MockSuperUser:
|
||||||
|
def has_perm(self, perm, obj=None):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
request = MockRequest()
|
||||||
|
request.user = MockSuperUser()
|
||||||
|
|
||||||
|
|
||||||
|
class Teamspeak3AdminTestCase(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.site = AdminSite()
|
||||||
|
cls.admin = AuthTSgroupAdmin(AuthTS, cls.site)
|
||||||
|
cls.group = Group.objects.create(name='test')
|
||||||
|
cls.ts_group = TSgroup.objects.create(ts_group_name='test')
|
||||||
|
|
||||||
|
def test_field_queryset_no_reserved_names(self):
|
||||||
|
"""Ensure all groups are listed when no reserved names"""
|
||||||
|
form = self.admin.get_form(request)
|
||||||
|
self.assertQuerysetEqual(form.base_fields['auth_group']._get_queryset(), Group.objects.all())
|
||||||
|
self.assertQuerysetEqual(form.base_fields['ts_group']._get_queryset(), TSgroup.objects.all())
|
||||||
|
|
||||||
|
def test_field_queryset_reserved_names(self):
|
||||||
|
"""Ensure reserved group names are filtered out"""
|
||||||
|
ReservedGroupName.objects.bulk_create([ReservedGroupName(name='test', reason='tests', created_by='Bob')])
|
||||||
|
form = self.admin.get_form(request)
|
||||||
|
self.assertQuerysetEqual(form.base_fields['auth_group']._get_queryset(), Group.objects.none())
|
||||||
|
self.assertQuerysetEqual(form.base_fields['ts_group']._get_queryset(), TSgroup.objects.none())
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import requests
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from allianceauth import NAME
|
from allianceauth import NAME
|
||||||
from allianceauth.eveonline.providers import provider
|
from allianceauth.srp.providers import esi
|
||||||
|
|
||||||
from .models import SrpUserRequest
|
from .models import SrpUserRequest
|
||||||
|
|
||||||
@@ -32,8 +32,7 @@ class SRPManager:
|
|||||||
if result:
|
if result:
|
||||||
killmail_id = result['killmail_id']
|
killmail_id = result['killmail_id']
|
||||||
killmail_hash = result['zkb']['hash']
|
killmail_hash = result['zkb']['hash']
|
||||||
c = provider.client
|
km = esi.client.Killmails.get_killmails_killmail_id_killmail_hash(
|
||||||
km = c.Killmails.get_killmails_killmail_id_killmail_hash(
|
|
||||||
killmail_id=killmail_id,
|
killmail_id=killmail_id,
|
||||||
killmail_hash=killmail_hash
|
killmail_hash=killmail_hash
|
||||||
).result()
|
).result()
|
||||||
|
|||||||
18
allianceauth/srp/providers.py
Normal file
18
allianceauth/srp/providers.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import os
|
||||||
|
from esi.clients import EsiClientProvider
|
||||||
|
|
||||||
|
from allianceauth import __version__
|
||||||
|
|
||||||
|
SWAGGER_SPEC = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||||
|
|
||||||
|
"""
|
||||||
|
Swagger spec operations:
|
||||||
|
get_killmails_killmail_id_killmail_hash
|
||||||
|
get_universe_types_type_id
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
esi = EsiClientProvider(
|
||||||
|
spec_file=SWAGGER_SPEC,
|
||||||
|
app_info_text=("allianceauth v" + __version__)
|
||||||
|
)
|
||||||
1
allianceauth/srp/swagger.json
Normal file
1
allianceauth/srp/swagger.json
Normal file
File diff suppressed because one or more lines are too long
@@ -267,7 +267,9 @@ ESC to cancel{% endblocktrans %}"id="blah"></i></th>
|
|||||||
"targets": [4, 5],
|
"targets": [4, 5],
|
||||||
"type": "num"
|
"type": "num"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"stateSave": true,
|
||||||
|
"stateDuration": 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// tooltip
|
// tooltip
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class TestSrpManager(TestCase):
|
|||||||
expected = 81973979
|
expected = 81973979
|
||||||
self.assertEqual(int(SRPManager.get_kill_id(link)), expected)
|
self.assertEqual(int(SRPManager.get_kill_id(link)), expected)
|
||||||
|
|
||||||
@patch(MODULE_PATH + '.provider')
|
@patch(MODULE_PATH + '.esi')
|
||||||
@patch(MODULE_PATH + '.requests.get')
|
@patch(MODULE_PATH + '.requests.get')
|
||||||
def test_can_get_kill_data(self, mock_get, mock_provider):
|
def test_can_get_kill_data(self, mock_get, mock_provider):
|
||||||
mock_get.return_value.json.return_value = load_data(
|
mock_get.return_value.json.return_value = load_data(
|
||||||
@@ -60,7 +60,7 @@ class TestSrpManager(TestCase):
|
|||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
SRPManager.get_kill_data(81973979)
|
SRPManager.get_kill_data(81973979)
|
||||||
|
|
||||||
@patch(MODULE_PATH + '.provider')
|
@patch(MODULE_PATH + '.esi')
|
||||||
@patch(MODULE_PATH + '.requests.get')
|
@patch(MODULE_PATH + '.requests.get')
|
||||||
def test_invalid_id_for_esi_raises_exception(
|
def test_invalid_id_for_esi_raises_exception(
|
||||||
self, mock_get, mock_provider
|
self, mock_get, mock_provider
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from django.utils import timezone
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from allianceauth.authentication.decorators import permissions_required
|
from allianceauth.authentication.decorators import permissions_required
|
||||||
from allianceauth.eveonline.providers import provider
|
from allianceauth.srp.providers import esi
|
||||||
from allianceauth.notifications import notify
|
from allianceauth.notifications import notify
|
||||||
from .form import SrpFleetMainForm
|
from .form import SrpFleetMainForm
|
||||||
from .form import SrpFleetMainUpdateForm
|
from .form import SrpFleetMainUpdateForm
|
||||||
@@ -201,7 +201,8 @@ def srp_request_view(request, fleet_srp):
|
|||||||
return redirect("srp:management")
|
return redirect("srp:management")
|
||||||
|
|
||||||
if request.user.character_ownerships.filter(character__character_id=str(victim_id)).exists():
|
if request.user.character_ownerships.filter(character__character_id=str(victim_id)).exists():
|
||||||
srp_request.srp_ship_name = provider.get_itemtype(ship_type_id).name
|
item_type = esi.client.Universe.get_universe_types_type_id(type_id=ship_type_id).result()
|
||||||
|
srp_request.srp_ship_name = item_type['name']
|
||||||
srp_request.kb_total_loss = ship_value
|
srp_request.kb_total_loss = ship_value
|
||||||
srp_request.post_time = post_time
|
srp_request.post_time = post_time
|
||||||
srp_request.save()
|
srp_request.save()
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ ul.list-group.list-group-horizontal > li.list-group-item {
|
|||||||
.table-aa > tbody > tr:last-child {
|
.table-aa > tbody > tr:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-status-progress-bar {
|
||||||
|
font-size: 15px!important;
|
||||||
|
line-height: normal!important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* highlight active menu items
|
/* highlight active menu items
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Alliance Auth customisations of the bootswatch Darkly theme
|
// Alliance Auth customisations of the bootswatch Darkly theme
|
||||||
// To build a new CSS file you need to `npm install -g less less-plugin-clean-css`
|
// To build a new CSS file you need to `npm install -g less less-plugin-clean-css`
|
||||||
// Then `lessc --clean-css darkly.less darkly.min.css`
|
// Then `lessc --clean-css="--advanced" darkly.less darkly.min.css`
|
||||||
|
|
||||||
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/bower_components/bootstrap/less/bootstrap.less";
|
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/bower_components/bootstrap/less/bootstrap.less";
|
||||||
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/darkly/variables.less";
|
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/darkly/variables.less";
|
||||||
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/darkly/bootswatch.less";
|
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/fa207fbbc80bd74339e179b054a322b092be98f6/darkly/bootswatch.less";
|
||||||
@import "../bootstrap-locals.less";
|
@import "../bootstrap-locals.less";
|
||||||
@import "../flatly-shared.less";
|
@import "../flatly-shared.less";
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,10 +1,10 @@
|
|||||||
// Alliance Auth customisations of the bootswatch Flatly theme
|
// Alliance Auth customisations of the bootswatch Flatly theme
|
||||||
// To build a new CSS file you need to `npm install -g less less-plugin-clean-css`
|
// To build a new CSS file you need to `npm install -g less less-plugin-clean-css`
|
||||||
// Then `lessc --clean-css flatly.less flatly.min.css`
|
// Then `lessc --clean-css="--advanced" flatly.less flatly.min.css`
|
||||||
|
|
||||||
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/bower_components/bootstrap/less/bootstrap.less";
|
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/bower_components/bootstrap/less/bootstrap.less";
|
||||||
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/flatly/variables.less";
|
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/flatly/variables.less";
|
||||||
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/v3/flatly/bootswatch.less";
|
@import "https://raw.githubusercontent.com/thomaspark/bootswatch/fa207fbbc80bd74339e179b054a322b092be98f6/flatly/bootswatch.less";
|
||||||
@import "../bootstrap-locals.less";
|
@import "../bootstrap-locals.less";
|
||||||
@import "../flatly-shared.less";
|
@import "../flatly-shared.less";
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,61 +1,20 @@
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
|
||||||
* check time
|
|
||||||
* @param i
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
let checkTime = function (i) {
|
|
||||||
if (i < 10) {
|
|
||||||
i = '0' + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* render a JS clock for Eve Time
|
* render a JS clock for Eve Time
|
||||||
* @param element
|
* @param element
|
||||||
* @param utcOffset
|
|
||||||
*/
|
*/
|
||||||
let renderClock = function (element, utcOffset) {
|
const renderClock = function (element) {
|
||||||
let today = new Date();
|
const datetimeNow = new Date();
|
||||||
let h = today.getUTCHours();
|
const h = String(datetimeNow.getUTCHours()).padStart(2, '0');
|
||||||
let m = today.getUTCMinutes();
|
const m = String(datetimeNow.getUTCMinutes()).padStart(2, '0');
|
||||||
let s = today.getUTCSeconds();
|
|
||||||
|
|
||||||
h = h + utcOffset;
|
element.html(h + ':' + m);
|
||||||
|
|
||||||
if (h > 24) {
|
|
||||||
h = h - 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (h < 0) {
|
|
||||||
h = h + 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
h = checkTime(h);
|
|
||||||
m = checkTime(m);
|
|
||||||
s = checkTime(s);
|
|
||||||
|
|
||||||
// document.getElementById('clock').innerHTML = h + ":" + m + ":" + s;
|
|
||||||
element.html(h + ':' + m + ':' + s);
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
renderClock(element, 0);
|
|
||||||
}, 500);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Start the Eve time clock in the top menu bar
|
||||||
* functions that need to be executed on load
|
setInterval(function () {
|
||||||
*/
|
renderClock($('.eve-time-wrapper .eve-time-clock'));
|
||||||
let init = function () {
|
}, 500);
|
||||||
renderClock($('.eve-time-wrapper .eve-time-clock'), 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* start the show
|
|
||||||
*/
|
|
||||||
init();
|
|
||||||
});
|
});
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
6
allianceauth/static/js/jquery-ui/1.12.1/css/jquery-ui.min.css
vendored
Normal file
6
allianceauth/static/js/jquery-ui/1.12.1/css/jquery-ui.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user