mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-09 12:30:15 +02:00
Merge branch 'v4.x' of gitlab.com:allianceauth/allianceauth into v4docs
This commit is contained in:
commit
445683c3d5
@ -19,5 +19,6 @@ exclude_lines =
|
||||
if __name__ == .__main__.:
|
||||
def __repr__
|
||||
raise AssertionError
|
||||
if TYPE_CHECKING:
|
||||
|
||||
ignore_errors = True
|
||||
|
@ -26,11 +26,11 @@ pre-commit-check:
|
||||
<<: *only-default
|
||||
stage: pre-commit
|
||||
image: python:3.11-bullseye
|
||||
variables:
|
||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
cache:
|
||||
paths:
|
||||
- ${PRE_COMMIT_HOME}
|
||||
# variables:
|
||||
# PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
# cache:
|
||||
# paths:
|
||||
# - ${PRE_COMMIT_HOME}
|
||||
script:
|
||||
- pip install pre-commit
|
||||
- pre-commit run --all-files
|
||||
|
@ -12,6 +12,9 @@ build:
|
||||
- redis
|
||||
tools:
|
||||
python: "3.11"
|
||||
jobs:
|
||||
post_system_dependencies:
|
||||
- redis-server --daemonize yes
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
|
@ -5,7 +5,7 @@ manage online service access.
|
||||
# This will make sure the app is always imported when
|
||||
# Django starts so that shared_task will use this app.
|
||||
|
||||
__version__ = '4.0.0a4'
|
||||
__version__ = '4.0.0b1'
|
||||
__title__ = 'Alliance Auth'
|
||||
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
||||
NAME = f'{__title__} v{__version__}'
|
||||
|
@ -1,6 +1,6 @@
|
||||
from allianceauth.hooks import DashboardItemHook
|
||||
from allianceauth import hooks
|
||||
from .views import dashboard_characters, dashboard_groups, dashboard_admin
|
||||
from .views import dashboard_characters, dashboard_esi_check, dashboard_groups, dashboard_admin
|
||||
|
||||
|
||||
class UserCharactersHook(DashboardItemHook):
|
||||
@ -26,6 +26,15 @@ class AdminHook(DashboardItemHook):
|
||||
DashboardItemHook.__init__(
|
||||
self,
|
||||
dashboard_admin,
|
||||
1
|
||||
)
|
||||
|
||||
|
||||
class ESICheckHook(DashboardItemHook):
|
||||
def __init__(self):
|
||||
DashboardItemHook.__init__(
|
||||
self,
|
||||
dashboard_esi_check,
|
||||
0
|
||||
)
|
||||
|
||||
@ -43,3 +52,8 @@ def register_groups_hook():
|
||||
@hooks.register('dashboard_hook')
|
||||
def register_admin_hook():
|
||||
return AdminHook()
|
||||
|
||||
|
||||
@hooks.register('dashboard_hook')
|
||||
def register_esi_hook():
|
||||
return ESICheckHook()
|
||||
|
12
allianceauth/authentication/constants.py
Normal file
12
allianceauth/authentication/constants.py
Normal file
@ -0,0 +1,12 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Overide ESI messages in the dashboard widget
|
||||
# when the returned messages are not helpful or out of date
|
||||
ESI_ERROR_MESSAGE_OVERRIDES = {
|
||||
420: _("This software has exceeded the error limit for ESI. "
|
||||
"If you are a user, please contact the maintainer of this software."
|
||||
" If you are a developer/maintainer, please make a greater "
|
||||
"effort in the future to receive valid responses. For tips on how, "
|
||||
"come have a chat with us in ##3rd-party-dev-and-esi on the EVE "
|
||||
"Online Discord. https://www.eveonline.com/discord")
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{% extends 'allianceauth/base.html' %}
|
||||
|
||||
|
||||
{% block page_title %}Dashboard{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<h1>Dashboard Dummy</h1>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,5 +1,5 @@
|
||||
{% load i18n %}
|
||||
<div class="col-12 col-xl-8 align-self-stretch p-2 ps-0 pe-0 ps-xl-0 pe-xl-2">
|
||||
<div id="aa-dashboard-panel-characters" class="col-12 col-xl-8 align-self-stretch p-2 ps-0 pe-0 ps-xl-0 pe-xl-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
@ -1,5 +1,5 @@
|
||||
{% load i18n %}
|
||||
<div class="col-12 col-xl-4 align-self-stretch py-2 ps-xl-2">
|
||||
<div id="aa-dashboard-panel-membership" class="col-12 col-xl-4 align-self-stretch py-2 ps-xl-2">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title text-center">{% translate "Membership" %}</h4>
|
@ -16,7 +16,7 @@
|
||||
{% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://community.eveonline.com/support/third-party-applications/ where possible."|urlize %}
|
||||
</p>
|
||||
|
||||
<table class="table" id="table_tokens" style="width: 100%;">
|
||||
<table class="table w-100" id="table_tokens">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Scopes" %}</th>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<select onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
|
||||
<select class="form-select" onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
|
||||
{% for language in languages %}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% extends 'public/base.html' %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load django_bootstrap5 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% translate "Registration" %}{% endblock %}
|
||||
@ -12,16 +12,20 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="panel panel-default panel-transparent">
|
||||
<div class="panel-body">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{ form|bootstrap }}
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Register" %}</button>
|
||||
</form>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-4">
|
||||
<div class="card card-login border-secondary p-3">
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
<button class="btn btn-primary btn-block" type="submit">{% translate "Register" %}</button>
|
||||
</form>
|
||||
|
||||
{% include 'public/lang_select.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'public/lang_select.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import json
|
||||
import requests_mock
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from allianceauth.authentication.views import task_counts
|
||||
from allianceauth.authentication.views import task_counts, esi_check
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
from allianceauth.authentication.constants import ESI_ERROR_MESSAGE_OVERRIDES
|
||||
|
||||
MODULE_PATH = "allianceauth.authentication.views"
|
||||
|
||||
@ -21,6 +23,8 @@ class TestRunningTasksCount(TestCase):
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
cls.user = AuthUtils.create_user("bruce_wayne")
|
||||
cls.user.is_superuser = True
|
||||
cls.user.save()
|
||||
|
||||
def test_should_return_data(
|
||||
self, mock_active_tasks_count, mock_queued_tasks_count
|
||||
@ -35,5 +39,164 @@ class TestRunningTasksCount(TestCase):
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {"tasks_running": 2, "tasks_queued": 3}
|
||||
jsonresponse_to_dict(response), {
|
||||
"tasks_running": 2, "tasks_queued": 3}
|
||||
)
|
||||
|
||||
def test_su_only(
|
||||
self, mock_active_tasks_count, mock_queued_tasks_count
|
||||
):
|
||||
self.user.is_superuser = False
|
||||
self.user.save()
|
||||
self.user.refresh_from_db()
|
||||
# given
|
||||
mock_active_tasks_count.return_value = 2
|
||||
mock_queued_tasks_count.return_value = 3
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = task_counts(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
|
||||
class TestEsiCheck(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
cls.user = AuthUtils.create_user("bruce_wayne")
|
||||
cls.user.is_superuser = True
|
||||
cls.user.save()
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_401_data_returns_200(
|
||||
self, m
|
||||
):
|
||||
error_json = {
|
||||
"error": "You have been banned from using ESI. Please contact Technical Support. (support@eveonline.com)"
|
||||
}
|
||||
status_code = 401
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(error_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": error_json
|
||||
}
|
||||
)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_504_data_returns_200(
|
||||
self, m
|
||||
):
|
||||
error_json = {
|
||||
"error": "Gateway timeout message",
|
||||
"timeout": 5000
|
||||
}
|
||||
status_code = 504
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(error_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": error_json
|
||||
}
|
||||
)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_420_data_override(
|
||||
self, m
|
||||
):
|
||||
error_json = {
|
||||
"error": "message from CCP",
|
||||
}
|
||||
status_code = 420
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(error_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotEqual(
|
||||
jsonresponse_to_dict(response)["data"],
|
||||
error_json
|
||||
)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": {
|
||||
"error": ESI_ERROR_MESSAGE_OVERRIDES.get(status_code)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_200_data_returns_200(
|
||||
self, m
|
||||
):
|
||||
good_json = {
|
||||
"players": 5,
|
||||
"server_version": "69420",
|
||||
"start_time": "2030-01-01T23:59:59Z"
|
||||
}
|
||||
status_code = 200
|
||||
|
||||
m.get(
|
||||
"https://esi.evetech.net/latest/status/?datasource=tranquility",
|
||||
text=json.dumps(good_json),
|
||||
status_code=status_code
|
||||
)
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertDictEqual(
|
||||
jsonresponse_to_dict(response), {
|
||||
"status": status_code,
|
||||
"data": good_json
|
||||
}
|
||||
)
|
||||
|
||||
def test_su_only(
|
||||
self,
|
||||
):
|
||||
self.user.is_superuser = False
|
||||
self.user.save()
|
||||
self.user.refresh_from_db()
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
request.user = self.user
|
||||
# when
|
||||
response = esi_check(request)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
@ -38,5 +38,7 @@ urlpatterns = [
|
||||
name='token_refresh'
|
||||
),
|
||||
path('dashboard/', views.dashboard, name='dashboard'),
|
||||
path('dashboard_bs3/', views.dashboard_bs3, name='dashboard_bs3'),
|
||||
path('task-counts/', views.task_counts, name='task_counts'),
|
||||
path('esi-check/', views.esi_check, name='esi_check'),
|
||||
]
|
||||
|
@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from allianceauth.hooks import get_hooks
|
||||
|
||||
import requests
|
||||
from django_registration.backends.activation.views import (
|
||||
REGISTRATION_SALT, ActivationView as BaseActivationView,
|
||||
RegistrationView as BaseRegistrationView,
|
||||
@ -10,7 +10,7 @@ from django_registration.signals import user_registered
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import authenticate, login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import signing
|
||||
from django.http import JsonResponse
|
||||
@ -23,14 +23,16 @@ from esi.decorators import token_required
|
||||
from esi.models import Token
|
||||
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
from allianceauth.hooks import get_hooks
|
||||
|
||||
from .constants import ESI_ERROR_MESSAGE_OVERRIDES
|
||||
from .core.celery_workers import active_tasks_count, queued_tasks_count
|
||||
from .forms import RegistrationForm
|
||||
from .models import CharacterOwnership
|
||||
|
||||
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||
_has_auto_groups = True
|
||||
from allianceauth.eveonline.autogroups.models import *
|
||||
from allianceauth.eveonline.autogroups.models import * # noqa: F401, F403
|
||||
else:
|
||||
_has_auto_groups = False
|
||||
|
||||
@ -54,7 +56,7 @@ def dashboard_groups(request):
|
||||
context = {
|
||||
'groups': groups,
|
||||
}
|
||||
return render_to_string('authentication/dashboard.groups.html', context=context, request=request)
|
||||
return render_to_string('authentication/dashboard_groups.html', context=context, request=request)
|
||||
|
||||
|
||||
def dashboard_characters(request):
|
||||
@ -66,7 +68,7 @@ def dashboard_characters(request):
|
||||
context = {
|
||||
'characters': characters
|
||||
}
|
||||
return render_to_string('authentication/dashboard.characters.html', context=context, request=request)
|
||||
return render_to_string('authentication/dashboard_characters.html', context=context, request=request)
|
||||
|
||||
|
||||
def dashboard_admin(request):
|
||||
@ -76,6 +78,13 @@ def dashboard_admin(request):
|
||||
return ""
|
||||
|
||||
|
||||
def dashboard_esi_check(request):
|
||||
if request.user.is_superuser:
|
||||
return render_to_string('allianceauth/admin-status/esi_check.html', request=request)
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
@login_required
|
||||
def dashboard(request):
|
||||
_dash_items = list()
|
||||
@ -135,23 +144,30 @@ def token_refresh(request, token_id=None):
|
||||
@login_required
|
||||
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
|
||||
def main_character_change(request, token):
|
||||
logger.debug(f"main_character_change called by user {request.user} for character {token.character_name}")
|
||||
logger.debug(
|
||||
f"main_character_change called by user {request.user} for character {token.character_name}")
|
||||
try:
|
||||
co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user)
|
||||
co = CharacterOwnership.objects.get(
|
||||
character__character_id=token.character_id, user=request.user)
|
||||
except CharacterOwnership.DoesNotExist:
|
||||
if not CharacterOwnership.objects.filter(character__character_id=token.character_id).exists():
|
||||
co = CharacterOwnership.objects.create_by_token(token)
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
_('Cannot change main character to %(char)s: character owned by a different account.') % ({'char': token.character_name})
|
||||
_('Cannot change main character to %(char)s: character owned by a different account.') % (
|
||||
{'char': token.character_name})
|
||||
)
|
||||
co = None
|
||||
if co:
|
||||
request.user.profile.main_character = co.character
|
||||
request.user.profile.save(update_fields=['main_character'])
|
||||
messages.success(request, _('Changed main character to %(char)s') % {"char": co.character})
|
||||
logger.info('Changed user %(user)s main character to %(char)s' % ({'user': request.user, 'char': co.character}))
|
||||
messages.success(request, _('Changed main character to %s') % co.character)
|
||||
logger.info(
|
||||
'Changed user {user} main character to {char}'.format(
|
||||
user=request.user, char=co.character
|
||||
)
|
||||
)
|
||||
return redirect("authentication:dashboard")
|
||||
|
||||
|
||||
@ -159,9 +175,11 @@ def main_character_change(request, token):
|
||||
def add_character(request, token):
|
||||
if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter(
|
||||
owner_hash=token.character_owner_hash).filter(user=request.user).exists():
|
||||
messages.success(request, _('Added %(name)s to your account.' % ({'name': token.character_name})))
|
||||
messages.success(request, _(
|
||||
'Added %(name)s to your account.' % ({'name': token.character_name})))
|
||||
else:
|
||||
messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % ({'name': token.character_name})))
|
||||
messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % (
|
||||
{'name': token.character_name})))
|
||||
return redirect('authentication:dashboard')
|
||||
|
||||
|
||||
@ -204,8 +222,10 @@ def sso_login(request, token):
|
||||
token.delete()
|
||||
messages.error(
|
||||
request,
|
||||
_('Unable to authenticate as the selected character. '
|
||||
'Please log in with the main character associated with this account.')
|
||||
_(
|
||||
'Unable to authenticate as the selected character. '
|
||||
'Please log in with the main character associated with this account.'
|
||||
)
|
||||
)
|
||||
return redirect(settings.LOGIN_URL)
|
||||
|
||||
@ -278,7 +298,8 @@ class RegistrationView(BaseRegistrationView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def register(self, form):
|
||||
user = User.objects.get(pk=self.request.session.get('registration_uid'))
|
||||
user = User.objects.get(
|
||||
pk=self.request.session.get('registration_uid'))
|
||||
user.email = form.cleaned_data['email']
|
||||
user_registered.send(self.__class__, user=user, request=self.request)
|
||||
if getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
|
||||
@ -295,7 +316,8 @@ class RegistrationView(BaseRegistrationView):
|
||||
|
||||
def get_email_context(self, activation_key):
|
||||
context = super().get_email_context(activation_key)
|
||||
context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key])
|
||||
context['url'] = context['site'].domain + \
|
||||
reverse('registration_activate', args=[activation_key])
|
||||
return context
|
||||
|
||||
|
||||
@ -328,20 +350,24 @@ class ActivationView(BaseActivationView):
|
||||
|
||||
|
||||
def registration_complete(request):
|
||||
messages.success(request, _('Sent confirmation email. Please follow the link to confirm your email address.'))
|
||||
messages.success(request, _(
|
||||
'Sent confirmation email. Please follow the link to confirm your email address.'))
|
||||
return redirect('authentication:login')
|
||||
|
||||
|
||||
def activation_complete(request):
|
||||
messages.success(request, _('Confirmed your email address. Please login to continue.'))
|
||||
messages.success(request, _(
|
||||
'Confirmed your email address. Please login to continue.'))
|
||||
return redirect('authentication:dashboard')
|
||||
|
||||
|
||||
def registration_closed(request):
|
||||
messages.error(request, _('Registration of new accounts is not allowed at this time.'))
|
||||
messages.error(request, _(
|
||||
'Registration of new accounts is not allowed at this time.'))
|
||||
return redirect('authentication:login')
|
||||
|
||||
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def task_counts(request) -> JsonResponse:
|
||||
"""Return task counts as JSON for an AJAX call."""
|
||||
data = {
|
||||
@ -349,3 +375,31 @@ def task_counts(request) -> JsonResponse:
|
||||
"tasks_queued": queued_tasks_count()
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
def check_for_override_esi_error_message(response):
|
||||
if response.status_code in ESI_ERROR_MESSAGE_OVERRIDES:
|
||||
return {"error": ESI_ERROR_MESSAGE_OVERRIDES.get(response.status_code)}
|
||||
else:
|
||||
return response.json()
|
||||
|
||||
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def esi_check(request) -> JsonResponse:
|
||||
"""Return if ESI ok With error messages and codes as JSON"""
|
||||
_r = requests.get("https://esi.evetech.net/latest/status/?datasource=tranquility")
|
||||
|
||||
data = {
|
||||
"status": _r.status_code,
|
||||
"data": check_for_override_esi_error_message(_r)
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@login_required
|
||||
def dashboard_bs3(request):
|
||||
"""Render dashboard view with BS3 theme.
|
||||
|
||||
This is an internal view used for testing BS3 backward compatibility in AA4 only.
|
||||
"""
|
||||
return render(request, 'authentication/dashboard_bs3.html')
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
<div class="card card-default mt-4">
|
||||
<div class="card-header clearfix" role="tablist">
|
||||
<ul class="nav nav-pills text-right float-start">
|
||||
<ul class="nav nav-pills float-start">
|
||||
<li class="nav-item" role="presentation">
|
||||
<a
|
||||
class="nav-link active"
|
||||
|
57
allianceauth/framework/api/evecharacter.py
Normal file
57
allianceauth/framework/api/evecharacter.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""
|
||||
Alliance Auth Evecharacter API
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from allianceauth.authentication.models import CharacterOwnership
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
from allianceauth.framework.api.user import get_sentinel_user
|
||||
|
||||
|
||||
def get_main_character_from_evecharacter(
|
||||
character: EveCharacter,
|
||||
) -> Optional[EveCharacter]:
|
||||
"""
|
||||
Get the main character for a given EveCharacter or None when no main character is set
|
||||
|
||||
:param character:
|
||||
:type character:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
try:
|
||||
userprofile = character.character_ownership.user.profile
|
||||
except (
|
||||
AttributeError,
|
||||
EveCharacter.character_ownership.RelatedObjectDoesNotExist,
|
||||
CharacterOwnership.user.RelatedObjectDoesNotExist,
|
||||
):
|
||||
return None
|
||||
|
||||
return userprofile.main_character
|
||||
|
||||
|
||||
def get_user_from_evecharacter(character: EveCharacter) -> User:
|
||||
"""
|
||||
Get the user for an EveCharacter or the sentinel user when no user is found
|
||||
|
||||
:param character:
|
||||
:type character:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
try:
|
||||
userprofile = character.character_ownership.user.profile
|
||||
except (
|
||||
AttributeError,
|
||||
EveCharacter.character_ownership.RelatedObjectDoesNotExist,
|
||||
CharacterOwnership.user.RelatedObjectDoesNotExist,
|
||||
):
|
||||
return get_sentinel_user()
|
||||
|
||||
return userprofile.user
|
@ -6,17 +6,33 @@ from typing import Optional
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from allianceauth.authentication.models import CharacterOwnership
|
||||
from allianceauth.eveonline.models import EveCharacter
|
||||
|
||||
|
||||
def get_sentinel_user() -> User:
|
||||
def get_all_characters_from_user(user: User) -> list:
|
||||
"""
|
||||
Get the sentinel user or create one
|
||||
Get all characters from a user or an empty list
|
||||
when no characters are found for the user or the user is None
|
||||
|
||||
:param user:
|
||||
:type user:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
|
||||
return User.objects.get_or_create(username="deleted")[0]
|
||||
if user is None:
|
||||
return []
|
||||
|
||||
try:
|
||||
characters = [
|
||||
char.character for char in CharacterOwnership.objects.filter(user=user)
|
||||
]
|
||||
except AttributeError:
|
||||
return []
|
||||
|
||||
return characters
|
||||
|
||||
|
||||
def get_main_character_from_user(user: User) -> Optional[EveCharacter]:
|
||||
"""
|
||||
@ -62,3 +78,13 @@ def get_main_character_name_from_user(user: User) -> str:
|
||||
return str(user)
|
||||
|
||||
return username
|
||||
|
||||
|
||||
def get_sentinel_user() -> User:
|
||||
"""
|
||||
Get the sentinel user or create one
|
||||
|
||||
:return:
|
||||
"""
|
||||
|
||||
return User.objects.get_or_create(username="deleted")[0]
|
||||
|
@ -13,6 +13,45 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Side Navigation
|
||||
------------------------------------------------------------------------------------- */
|
||||
@media all {
|
||||
#sidebar > div {
|
||||
width: 325px;
|
||||
}
|
||||
|
||||
/* Menu items in general */
|
||||
#sidebar-menu li > a,
|
||||
#sidebar-menu li > ul > li > a {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
max-width: 210px;
|
||||
}
|
||||
|
||||
/* Parent items with chevron and possible badge */
|
||||
#sidebar-menu li:has(span.badge) > a[data-bs-toggle="collapse"] {
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
/* Child items with possible badge */
|
||||
#sidebar-menu li > ul > li > a {
|
||||
max-width: 189px;
|
||||
}
|
||||
|
||||
/* Chevron icons */
|
||||
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-down,
|
||||
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-right {
|
||||
display: block;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-right,
|
||||
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-down {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cursor classes
|
||||
------------------------------------------------------------------------------------- */
|
||||
@media all {
|
||||
@ -72,9 +111,16 @@
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border-left-width: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
margin-top: 1.25rem;
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.aa-callout.aa-callout-sm {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aa-callout.aa-callout-lg {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Last item bottom margin should be 0 */
|
||||
|
@ -71,7 +71,7 @@
|
||||
{% block extra_javascript %}
|
||||
{% include 'bundles/datatables-js-bs5.html' %}
|
||||
{% include 'bundles/moment-js.html' with locale=True %}
|
||||
{# {% include 'bundles/filterdropdown-js.html' %}#}
|
||||
{% include 'bundles/filterdropdown-js.html' %}
|
||||
|
||||
<script>
|
||||
$.fn.dataTable.moment = (format, locale) => {
|
||||
@ -117,7 +117,8 @@
|
||||
idx: 6
|
||||
}
|
||||
],
|
||||
bootstrap: true
|
||||
bootstrap: true,
|
||||
bootstrap_version: 5
|
||||
},
|
||||
"stateSave": true,
|
||||
"stateDuration": 0
|
||||
|
@ -30,7 +30,11 @@
|
||||
<tr>
|
||||
<th>{% translate "Name" %}</th>
|
||||
<th>{% translate "Description" %}</th>
|
||||
<th>{% translate "Leaders" %}<span class="m-1 fw-lighter badge bg-primary">{% translate "User" %}</span><span class="m-1 fw-lighter badge bg-secondary ">{% translate "Group" %}</span></th>
|
||||
<th>
|
||||
{% translate "Leaders" %}<br>
|
||||
<span class="my-1 me-1 fw-lighter badge bg-primary">{% translate "User" %}</span>
|
||||
<span class="my-1 me-1 fw-lighter badge bg-secondary">{% translate "Group" %}</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -39,13 +43,23 @@
|
||||
{% for g in groups %}
|
||||
<tr>
|
||||
<td>{{ g.group.name }}</td>
|
||||
<td>{{ g.group.authgroup.description|linebreaks|urlize }}</td>
|
||||
<td>
|
||||
{% if g.group.authgroup.description %}
|
||||
{{ g.group.authgroup.description|linebreaks|urlize }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="max-width: 30%;">
|
||||
{% if g.group.authgroup.group_leaders.all.count %}
|
||||
{% for leader in g.group.authgroup.group_leaders.all %}{% if leader.profile.main_character %}<span class="m-1 badge bg-primary">{{leader.profile.main_character}}</span>{% endif %}{% endfor %}
|
||||
{% for leader in g.group.authgroup.group_leaders.all %}
|
||||
{% if leader.profile.main_character %}
|
||||
<span class="my-1 me-1 badge bg-primary">{{leader.profile.main_character}}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if g.group.authgroup.group_leaders.all.count %}
|
||||
{% for group in g.group.authgroup.group_leader_groups.all %}<span class="badge bg-secondary">{{group.name}}</span>{% endfor %}
|
||||
{% for group in g.group.authgroup.group_leader_groups.all %}
|
||||
<span class="my-1 me-1 badge bg-secondary">{{group.name}}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
|
Binary file not shown.
@ -13,8 +13,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Peter Pfeufer, 2023\n"
|
||||
"Language-Team: German (https://app.transifex.com/alliance-auth/teams/107430/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -31,7 +31,7 @@ msgstr "Google Analytics Universal"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google Analytics V4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr ""
|
||||
"Zur Ausführung dieser Aktion ist ein Hauptcharakter erforderlich. Füge unten"
|
||||
@ -47,63 +47,68 @@ msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr ""
|
||||
"Du kannst diese eingeschränkten Gruppen nicht hinzufügen oder entfernen: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "Englisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "Deutsch"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "Spanisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Chinesisch vereinfacht"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "Russisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "Koreanisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "Französisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "Japanisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "Italienisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr "Ukrainisch"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "Nachtmodus"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "Status geändert zu %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "Dein Nutzerstatus ist nun %(state)s"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "Dashboard"
|
||||
@ -161,8 +166,54 @@ msgstr "Corp"
|
||||
msgid "Alliance"
|
||||
msgstr "Allianz"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr "Token-Verwaltung"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr "Geltungsbereiche"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Charakter"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
"Diese Seite ist der beste Versuch, aber Sicherungen oder Datenbankprotokolle"
|
||||
" können weiterhin Deine Token enthalten. Widerrufe die Token nach "
|
||||
"Möglichkeit immer auf https://community.eveonline.com/support/third-party-"
|
||||
"applications/."
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "Einloggen"
|
||||
|
||||
@ -195,7 +246,7 @@ msgstr "Registrieren"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "Ungültiger oder abgelaufener Aktivierungslink."
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
@ -204,32 +255,34 @@ msgstr ""
|
||||
"Der Haputcharakter kann nicht zu %(char)s geändert werden. Dieser Charakter "
|
||||
"gehört zu einem anderen Konto."
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "Haupcharakter zu %(char)s geändert"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "%(name)s zu Deinem Konto hinzugefügt."
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr ""
|
||||
"Es ist nicht möglich %(name)s zu Deinem Konto hinzu zu fügen: Dieser hat "
|
||||
"bereits ein eigenes Konto."
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "Authentifizierung mit dem ausgewählten Charakter nicht möglich."
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "Token zur Registrierung ist abgelaufen."
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
@ -237,11 +290,11 @@ msgstr ""
|
||||
"Bestätigungs-E-Mail gesendet. Bitte folge dem Link, um Deine E-Mail-Adresse "
|
||||
"zu bestätigen."
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr "Deine E-Mail Adresse wurde bestätigt. Bitte einloggen zum Fortfahren."
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "Registrierung von neuen Konten ist zur Zeit nicht erlaubt."
|
||||
|
||||
@ -284,19 +337,6 @@ msgstr "Nicht registriert"
|
||||
msgid "Last update:"
|
||||
msgstr "Letzte Aktualisierung:"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Charakter"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -630,20 +670,25 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "Gruppenverwaltung"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Nutzer"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
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/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(automatisch)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "Es existiert bereits eine Gruppe mit diesem Namen."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -653,13 +698,13 @@ msgstr ""
|
||||
"<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:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
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:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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."
|
||||
@ -668,7 +713,7 @@ msgstr ""
|
||||
"Anfrage.<br>Wenn die Gruppe nicht offen ist, müssen Anfragen manuell "
|
||||
"bestätigt werden."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -680,7 +725,7 @@ msgstr ""
|
||||
" wird Nutzer nicht von dieser Gruppe entfernen wenn diese nicht länger "
|
||||
"authentifiziert sind."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
@ -688,7 +733,7 @@ msgstr ""
|
||||
"Gruppe ist eingeschränkt. Das bedeutet, dass zum Hinzufügen oder Entfernen "
|
||||
"von Benutzern für diese Gruppe ein Superuser/Administrator erforderlich ist."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
msgid ""
|
||||
"Group leaders can process requests for this group. Use the "
|
||||
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||
@ -698,7 +743,7 @@ msgstr ""
|
||||
"<code>auth.group_management</code> Berechtigung um Nutzern zu erlauben alle "
|
||||
"Gruppen zu verwalten<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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 "
|
||||
@ -708,7 +753,7 @@ msgstr ""
|
||||
"Nutze die <code>auth.group_management</code> Berechtigung um Nutzern zu "
|
||||
"erlauben alle Gruppen zu verwalten.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
@ -716,42 +761,42 @@ msgstr ""
|
||||
"Die hier aufgeführten Status können dieser Gruppe beitreten, sofern sie über"
|
||||
" die entsprechenden Berechtigungen verfügen.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
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:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "Kann nicht öffentlichen Gruppen beitreten"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "Name"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "Name kann nicht für Gruppen genutzt werden"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "Grund"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "Grund wieso dieser Name reserviert ist."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "Erstellt bei"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "Erstellt"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "Datum der Erstellung dieses Eintrags"
|
||||
|
||||
@ -978,26 +1023,26 @@ msgstr "Gruppenanfragen"
|
||||
msgid "Group Membership"
|
||||
msgstr "Gruppenmitgliedschaft"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "Benutzer %(user)s von Gruppe %(group)s entfernt."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "Benutzer existiert nicht in dieser Gruppe"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "Gruppe existiert nicht"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "Beitrittsanfrage von %(mainchar)s zur Gruppe %(group)s akzeptiert."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1006,18 +1051,18 @@ msgstr ""
|
||||
"Bei der Bearbeitung des Beitrittsanfrage von %(mainchar)s zur Gruppe "
|
||||
"%(group)s ist ein unbehandelter Fehler aufgetreten."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "Beitrittsanfrage von %(mainchar)s zur Gruppe %(group)s abgelehnt."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "Austrittsanfrage von %(mainchar)s für Gruppe %(group)s akzeptiert."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1026,42 +1071,42 @@ msgstr ""
|
||||
"Bei der Bearbeitung des Austrittsanfrage von %(mainchar)s für Gruppe "
|
||||
"%(group)s ist ein unbehandelter Fehler aufgetreten."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "Austrittsanfrage von %(mainchar)s für Gruppe %(group)s abgelehnt."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "Du kannst dieser Gruppe nicht beitreten"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "Du bist bereits Mitglied dieser Gruppe."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "Du hast bereits für diese Gruppe angefragt."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "Beitritt zur Gruppe %(group)s beantragt."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "Du kannst diese Gruppe nicht verlassen"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "Du bist kein Mitglied dieser Gruppe"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "Du hast bereits eine ausstehendes Austrittsanfrage für diese Gruppe."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "Austrittsanfrage für Gruppe %(group)s gesendet."
|
||||
@ -1123,16 +1168,6 @@ msgstr "Erstelle Bewerbung"
|
||||
msgid "Username"
|
||||
msgstr "Benutzername"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1471,10 +1506,6 @@ msgstr "Model"
|
||||
msgid "Code Name"
|
||||
msgstr "Code Name"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Nutzer"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "Status"
|
||||
@ -2218,14 +2249,12 @@ msgstr ""
|
||||
"Status von %(total)s verarbeiteten Tasks • innerhalb der letzten %(latest)s"
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"%(queue_length)s Tasks in der Warteschlange"
|
||||
msgid "running"
|
||||
msgstr "laufend"
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr "eingereiht"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
msgid "Admin"
|
||||
@ -2240,11 +2269,11 @@ msgid "AA Support Discord"
|
||||
msgstr "AA Support Discord"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "Nutzermenü"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "Ausloggen"
|
||||
|
||||
@ -2300,22 +2329,30 @@ msgid "Objective"
|
||||
msgstr "Ziel"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr "Absoluter Timer"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr "Datum und Uhrzeit"
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "Tage verbleibend"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "Stunden verbleibend"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "Minuten verbleibend"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "Wichtig"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "Auf Corp beschränkt"
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -6,18 +6,18 @@
|
||||
# Translators:
|
||||
# Fegpawn Kaundur, 2023
|
||||
# frank1210 <francolopez_16@hotmail.com>, 2023
|
||||
# trenus, 2023
|
||||
# Joel Falknau <ozirascal@gmail.com>, 2023
|
||||
# Young Anexo, 2023
|
||||
# Joel Falknau <ozirascal@gmail.com>, 2023
|
||||
# trenus, 2023
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"Last-Translator: Young Anexo, 2023\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: trenus, 2023\n"
|
||||
"Language-Team: Spanish (https://app.transifex.com/alliance-auth/teams/107430/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -33,7 +33,7 @@ msgstr "Google Analytics Universal"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google Analytics V4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr ""
|
||||
"Se necesita un personaje principal para realizar esa acción. Añade uno a "
|
||||
@ -48,63 +48,68 @@ msgstr "E-mail"
|
||||
msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr "No puedes añadir o eliminar estos grupos restringidos: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "Inglés"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "Alemán"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "Español"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Chino Simplificado"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "Ruso"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "Coreano"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "Francés"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "Japonés"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "Italiano"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "Modo Nocturno"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "Estado cambiado a: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "El estado de su usuario es ahora: %(state)s"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "Página principal"
|
||||
@ -162,8 +167,50 @@ msgstr "Corporación"
|
||||
msgid "Alliance"
|
||||
msgstr "Allianza"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Personaje"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "Ingresar"
|
||||
|
||||
@ -197,7 +244,7 @@ msgstr "Registrar"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "Enlace de activacion expirado o invalido"
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
@ -206,45 +253,47 @@ msgstr ""
|
||||
"No se puede cambiar de personaje principal a %(char)s: personaje "
|
||||
"perteneciente a otra cuenta."
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "Se ha cambiado tu personaje principal ha %(char)s"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "Se ha agregado a %(name)s a tu cuenta"
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr ""
|
||||
"Se fallo en agregar a %(name)s a tu cuenta: Ya se encuentra registrado en "
|
||||
"otra cuenta."
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "Imposible validar con el personaje seleccionado."
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "El token de registracion expiro."
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
msgstr ""
|
||||
"Confirmacion de mail enviada. Por favor siga el enlace para confirmar "
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr ""
|
||||
"Se ha confirmado su direccion de mail. Por favor igrese su token para "
|
||||
"continuar."
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "En este momento no se permite el registro de nuevas cuentas."
|
||||
|
||||
@ -287,19 +336,6 @@ msgstr "Sin registro"
|
||||
msgid "Last update:"
|
||||
msgstr "Ultima Actualizacion:"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Personaje"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -636,19 +672,24 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "Manejo de Grupo"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Usuarios"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr "Este nombre ha sido reservado y no puede utilizarse para grupos."
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(auto)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "Ya existe un grupo con ese nombre."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -658,13 +699,13 @@ msgstr ""
|
||||
"grupo.<br>Se utiliza para grupos como Miembros, Corp_*, Alliance_*, "
|
||||
"etc.<br><b>Anula las opciones Oculto y Abierto cuando se selecciona.</b>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr ""
|
||||
"El grupo está oculto para los usuarios, pero aún pueden unirse con el enlace"
|
||||
" correcto."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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."
|
||||
@ -673,7 +714,7 @@ msgstr ""
|
||||
"soliciten.<br>Si el grupo no está abierto, los usuarios necesitarán que su "
|
||||
"solicitud sea aprobada manualmente."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -685,7 +726,7 @@ msgstr ""
|
||||
"grupo.<br>Auth no eliminará automáticamente a los usuarios de este grupo "
|
||||
"cuando ya no estén autenticados."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
@ -693,7 +734,7 @@ msgstr ""
|
||||
"El grupo está restringido. Esto significa que para añadir o eliminar "
|
||||
"usuarios de este grupo se requiere un superusuario administrador."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
msgid ""
|
||||
"Group leaders can process requests for this group. Use the "
|
||||
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||
@ -703,7 +744,7 @@ msgstr ""
|
||||
" permiso <code>auth.group_management</code> para permitir que un usuario "
|
||||
"gestione todos los grupos.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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 "
|
||||
@ -713,7 +754,7 @@ msgstr ""
|
||||
"grupo. Utilice el <code>permiso auth.group_management</code> para permitir "
|
||||
"que un usuario gestione todos los grupos.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
@ -721,42 +762,42 @@ msgstr ""
|
||||
"Los estados que figuren en esta lista podrán unirse a este grupo siempre que"
|
||||
" dispongan de los permisos adecuados.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr ""
|
||||
"Breve descripción <i>(máx. 512 caracteres)</i> del grupo que se muestra a "
|
||||
"los usuarios."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "Se pueden solicitar grupos no públicos"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "nombre"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "Nombre que no se puede utilizar para los grupos."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "razón"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "Razón por la que este nombre está reservado."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "creado por"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "creado en"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "Fecha de creación de esta entrada"
|
||||
|
||||
@ -983,26 +1024,26 @@ msgstr "Solicitudes de Grupo"
|
||||
msgid "Group Membership"
|
||||
msgstr "Membresia de Grupo"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "El usuario %(user)s fue removido del grupo %(group)s"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "El usuario no existe en ese grupos"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "El grupo no existe"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "Solicitud aceptada de %(mainchar)s a %(group)s"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1011,18 +1052,18 @@ msgstr ""
|
||||
"Ocurrio un error cuando se intento procesar la informacion de %(mainchar)s "
|
||||
"al grupo %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "Se rechazo la solicitud de %(mainchar)s al grupo %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "Se acepto la solicitud de %(mainchar)s para dejar el grupo %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1031,43 +1072,43 @@ msgstr ""
|
||||
"Se ha producido un error al procesar la solicitud de %(mainchar)s para "
|
||||
"abandonar %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr ""
|
||||
"Se rechazo la solicitud de %(mainchar)s para dejar el grupo %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "No puedes unirte a ese grupo"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "Ya eres miembro de ese grupo."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "Ya tiene una solicitud pendiente para ese grupo."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "Solicitud enviada al grupo %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "No puedes dejar el grupos"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "No eres miembro de ese grupo"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "Ya tiene una solicitud de baja pendiente para ese grupo."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "Solicitaste dejar el grupo %(group)s."
|
||||
@ -1129,16 +1170,6 @@ msgstr "Crear Solicitud"
|
||||
msgid "Username"
|
||||
msgstr "Usuario"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1477,10 +1508,6 @@ msgstr "Modelo"
|
||||
msgid "Code Name"
|
||||
msgstr "Nombre codigo"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Usuarios"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "Estados"
|
||||
@ -2219,14 +2246,12 @@ msgstr ""
|
||||
"Estado de %(total)s tareas procesadas • últimos %(latest)s"
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"%(queue_length)s tareas en cola"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
msgid "Admin"
|
||||
@ -2241,11 +2266,11 @@ msgid "AA Support Discord"
|
||||
msgstr "Soporte Discord AA"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "Menú de usuario"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "Salir"
|
||||
|
||||
@ -2301,22 +2326,30 @@ msgid "Objective"
|
||||
msgstr "Objetivo"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "Dias restantes"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "Horas Restantes"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "Minutos Restantes"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "Importante"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "Restringido a Corp"
|
||||
|
||||
|
Binary file not shown.
@ -4,23 +4,23 @@
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Keven D. <theenarki@gmail.com>, 2023
|
||||
# rockclodbuster, 2023
|
||||
# Geoffrey Fabbro, 2023
|
||||
# Mohssine Daghghar, 2023
|
||||
# François LACROIX-DURANT <umbre@fallenstarscreations.com>, 2023
|
||||
# Mickael PATTE, 2023
|
||||
# Philippe Querin-Laporte <philippe.querin@hotmail.com>, 2023
|
||||
# Mickael Gr4vity, 2023
|
||||
# Idea ., 2023
|
||||
# rockclodbuster, 2023
|
||||
# Keven D. <theenarki@gmail.com>, 2023
|
||||
# Mohssine Daghghar, 2023
|
||||
# Philippe Querin-Laporte <philippe.querin@hotmail.com>, 2023
|
||||
# draktanar KarazGrong <umbre@fallenstarscreations.com>, 2023
|
||||
# Geoffrey Fabbro, 2023
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"Last-Translator: Idea ., 2023\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Geoffrey Fabbro, 2023\n"
|
||||
"Language-Team: French (France) (https://app.transifex.com/alliance-auth/teams/107430/fr_FR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -36,7 +36,7 @@ msgstr "Google Analytique Universelle"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google Analytics V4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr ""
|
||||
"Un personnage principal est nécessaire pour effectuer cette action. Ajoutez-"
|
||||
@ -53,63 +53,68 @@ msgstr ""
|
||||
"Vous n'avez pas l’autorisation d'ajouter ou d'enlever ces groupes "
|
||||
"restreints: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "Anglais"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "Allemand"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "Espagnol"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Chinois simplifié"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "Russe"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "Coréen"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "Français"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "Japonais"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "Italien"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "Langue"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "Mode Nuit"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "État changé à: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "L'état de votre personnage est maintenant: %(state)s"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "Écran de bord"
|
||||
@ -167,8 +172,50 @@ msgstr "Corpo"
|
||||
msgid "Alliance"
|
||||
msgstr "Alliance"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Personnage"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "Connexion"
|
||||
|
||||
@ -202,7 +249,7 @@ msgstr "S'inscrire"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "Lien d'activation invalide ou expiré."
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
@ -211,30 +258,32 @@ msgstr ""
|
||||
"Impossible de changer le personnage principal à %(char)s. Le personnage "
|
||||
"appartient à un autre compte."
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "Changé le personnage principal à %(char)s"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "Ajouté %(name)s à votre compte."
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr "Impossible d'ajouter %(name)s à votre compte: ils ont déjà un compte."
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "Personnage principal : échec de l'identification."
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "Le token d'enregistrement est expiré."
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
@ -242,12 +291,12 @@ msgstr ""
|
||||
"Email de confirmation envoyé. Cliquez sur le lien pour valider votre adresse"
|
||||
" email."
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr ""
|
||||
"Votre adresse email a été confirmé. Veuillez vous connecter pour continuer."
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "La création de nouveaux comptes n'est pas actuellement permise."
|
||||
|
||||
@ -290,19 +339,6 @@ msgstr "Pas inscrit"
|
||||
msgid "Last update:"
|
||||
msgstr "Dernière mise à jour:"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Personnage"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -639,19 +675,24 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "Gestion de groupe"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr "Ce nom a été réserver et il ne peut être utilisé pour les groupes."
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(automatique)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "Il existe déjà un groupe portant ce nom."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -662,13 +703,13 @@ msgstr ""
|
||||
"Corporations _*, Alliance etc.<br><b> Annule les options masquer et exposer "
|
||||
"quand sélectionner."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr ""
|
||||
"Le groupe est caché aux utilisateurs, mais ils peuvent toujours rejoindre "
|
||||
"avec le bon lien."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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."
|
||||
@ -677,7 +718,7 @@ msgstr ""
|
||||
" demande. <br> Si le groupe n’est pas ouvert, les utilisateurs auront besoin"
|
||||
" que leurs demandes soit approuvées manuellement."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -689,7 +730,7 @@ msgstr ""
|
||||
"groupe.<br> L' Auth ne supprimera pas automatiquement les utilisateurs de ce"
|
||||
" groupe lorsqu’ils ne seront plus authentifiés."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
@ -697,62 +738,62 @@ msgstr ""
|
||||
"Le groupe est restreint. Cela signifie que l’ajout ou la suppression "
|
||||
"d’utilisateurs pour ce groupe nécessite un administrateur superutilisateur."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
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:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr ""
|
||||
"Brève description <i> (512 caractères maximum) </i> du groupe présenté aux "
|
||||
"utilisateurs."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "Peut demander des groupes non publics"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "Nom qui ne peut pas être utilisé pour les groupes."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "raison"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "Raison pour laquelle ce nom est réservé."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "créé par"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "créé à"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "Date de création de cette entrée"
|
||||
|
||||
@ -979,26 +1020,26 @@ msgstr "Demandes de groupe"
|
||||
msgid "Group Membership"
|
||||
msgstr "Groupe appartenance "
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "L'utilisateur %(user)s à été retiré du groupe %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "L'utilisateur n'existe pas dans ce groupe"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "Groupe non-existant"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "Candidature de %(mainchar)s acceptée à %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1007,18 +1048,18 @@ msgstr ""
|
||||
"Une erreur inattendue est survenue durant le traitement de l'application de "
|
||||
"%(mainchar)s à %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "L'application de %(mainchar)s à %(group)s est rejetée."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "La demande de retirer %(mainchar)s de %(group)s est acceptée."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1027,42 +1068,42 @@ msgstr ""
|
||||
"Une erreur inattendue est survenue durant le traitement de la demande de "
|
||||
"retirer %(mainchar)s de %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "La remande de retirer %(mainchar)s de %(group)s a été refusée."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "Vous ne pouvez pas joindre ce groupe."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "Vous faites déjà parti de ce groupe."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "Vous avez déjà une application en attente pour joindre ce groupe."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "Appliqué au groupe %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "Vous ne pouvez pas quitter ce groupe."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "Vous n'êtes pas un membre de ce groupe."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "Vous avec déjà une demande de quitter ce groupe en attente."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "Appliqué pour quitter le groupe %(group)s."
|
||||
@ -1124,16 +1165,6 @@ msgstr "Créer une application"
|
||||
msgid "Username"
|
||||
msgstr "Nom d'utilisateur"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1472,10 +1503,6 @@ msgstr "Modèle"
|
||||
msgid "Code Name"
|
||||
msgstr "Nom De Code"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "États"
|
||||
@ -2217,15 +2244,12 @@ msgstr ""
|
||||
" "
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
"\n"
|
||||
" %(queue_length)stâches en file d'attente\n"
|
||||
" "
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
msgid "Admin"
|
||||
@ -2240,11 +2264,11 @@ msgid "AA Support Discord"
|
||||
msgstr "Support Discord AA"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "Menu Utilisateur"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
@ -2300,22 +2324,30 @@ msgid "Objective"
|
||||
msgstr "Objectif"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "Jour restants"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "Heures restantes"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "Minutes restantes"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "Important"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "Limité à la Corporation"
|
||||
|
||||
|
Binary file not shown.
@ -12,8 +12,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Linus Hope, 2023\n"
|
||||
"Language-Team: Italian (Italy) (https://app.transifex.com/alliance-auth/teams/107430/it_IT/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -30,7 +30,7 @@ msgstr "Google Analytics Universal"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google Analytics V4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr ""
|
||||
"Per completare questa azione è necessario un personaggio principale. "
|
||||
@ -45,63 +45,68 @@ msgstr "Indirizzo di posta elettronica"
|
||||
msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr "Non ti è consentito aggiungere o rimuovere questi gruppi limitati:%s"
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "Inglese"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "Tedesco"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "Spagnolo"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Cinese semplificato"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "Russo"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "Coreano"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "Francese"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "Giapponese"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "Italiano"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "Lingua"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "Modalità scura"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "Stato modificato a: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "Il tuo stato utente è ora: %(state)s"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "Pannello di controllo"
|
||||
@ -160,8 +165,50 @@ msgstr "Corporazione"
|
||||
msgid "Alliance"
|
||||
msgstr "Alleanza"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Azioni"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Personaggio"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "Accedi"
|
||||
|
||||
@ -195,7 +242,7 @@ msgstr "Registrati"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "Il link di attivazione è invalido o scaduto."
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
@ -204,32 +251,34 @@ msgstr ""
|
||||
"Il seguente personaggio %(char)s non può essere reso principale: è già "
|
||||
"utilizzato da un altro profilo."
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "Hai reso personaggio principale: %(char)s"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "%(name)s è stato aggiunto al tuo profilo."
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr ""
|
||||
"Impossibile aggiungere %(name)s al tuo profilo: quel personaggio è già "
|
||||
"collegato ad un altro profilo."
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "Impossibile autenticarsi con il personaggio selezionato."
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "Il token di registrazione è scaduto."
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
@ -237,13 +286,13 @@ msgstr ""
|
||||
"Una e-mail di conferma è stata inviata. Per favore, utilizza il link per "
|
||||
"confermare il tuo indirizzo di posta elettronica."
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr ""
|
||||
"Il tuo indirizzo di posta elettronica è stato confermato. Per favore accedi "
|
||||
"per continuare."
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "Al momento non è possibile registrare nuovi profili."
|
||||
|
||||
@ -286,19 +335,6 @@ msgstr "Non registrati"
|
||||
msgid "Last update:"
|
||||
msgstr "Ultimo aggiornamento:"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Personaggio"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -638,19 +674,24 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "Gestione gruppi"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Utenti"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr "Questo nome è riservato e non può essere utilizzato per gruppi."
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(auto)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "Esiste già un gruppo con quel nome."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -661,13 +702,13 @@ msgstr ""
|
||||
"Alleanze, etc. <br><b>Sovrascrive opzioni nascoste e aperte quando "
|
||||
"seleazionato."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr ""
|
||||
"Il gruppo è nascosto agli utenti ma questi vi possono aderire utilizzando il"
|
||||
" link corretto. "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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."
|
||||
@ -676,7 +717,7 @@ msgstr ""
|
||||
"di richiesta. <br>Se il gruppo non è aperto gli utenti necessiteranno di "
|
||||
"approvazione manuale."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -688,7 +729,7 @@ msgstr ""
|
||||
"gruppo. <br>Auth non rimuoverà automaticamente gli utenti da questo gruppo "
|
||||
"quando non sono più autenticati."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
@ -696,7 +737,7 @@ msgstr ""
|
||||
"Il gruppo è limitato. Ciò significa che l'aggiunta o la rimozione di utenti "
|
||||
"per questo gruppo richiede un amministratore."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
msgid ""
|
||||
"Group leaders can process requests for this group. Use the "
|
||||
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||
@ -706,7 +747,7 @@ msgstr ""
|
||||
"l'<code>autorizzazione auth.group_management </code> per consentire ad un "
|
||||
"utente di gestire tutti i gruppi. <br> "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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 "
|
||||
@ -716,7 +757,7 @@ msgstr ""
|
||||
" Usa l'<code>autorizzazione auth.group_management </code> per consentire ad "
|
||||
"un utente di gestire tutti i gruppi. <br> "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
@ -724,42 +765,42 @@ msgstr ""
|
||||
"Gli stati qui elencati avranno la possibilità di aderire a questo gruppo a "
|
||||
"condizione che dispongano delle opportune autorizzazioni. <br> "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr ""
|
||||
"Breve descrizione <i>(max. 512 caratteri)</i> del gruppo da mostrare agli "
|
||||
"utenti."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "Può fare richiesta a gruppi non pubblici"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "nome"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "Il nome scelto non può essere utilizzato per gruppi."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "ragione"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "Ragione per la quale questo nome è riservato."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "creato da"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "creato il"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "Data in cui è stata creata questa voce."
|
||||
|
||||
@ -986,26 +1027,26 @@ msgstr "Richieste"
|
||||
msgid "Group Membership"
|
||||
msgstr "Membri del gruppo"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "Rimosso il membro %(user)s dal gruppo %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "L’utente non fa parte del gruppo selezionato"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "Il gruppo non esiste"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "La domanda di %(mainchar)s per %(group)s è stata accettata."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1014,20 +1055,20 @@ msgstr ""
|
||||
"Si è verificato un’errore durante l’elaborazione della domanda di "
|
||||
"%(mainchar)s per %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "La domanda di %(mainchar)s per %(group)s è stata rifiutata."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr ""
|
||||
"La domanda di congedo da parte di %(mainchar)s per %(group)s è stata "
|
||||
"accettata."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1036,45 +1077,45 @@ msgstr ""
|
||||
"Si è verificato un’errore durante l’elaborazione della domanda di congedo da"
|
||||
" parte di %(mainchar)s per %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr ""
|
||||
"La domanda di congedo da parte di %(mainchar)s per %(group)s è stata "
|
||||
"rifiutata."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "Non puoi unirti a questo gruppo"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "Fai già parte del gruppo selezionato."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr ""
|
||||
"Hai già una candidatura in attesa di essere elaborata per questo gruppo"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "Hai fatto domanda per il gruppo %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "Non puoi lasciare questo gruppo."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "Non sei un membro di questo gruppo."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "Hai già una richiesta di congedo in sospeso per quel gruppo."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "Hai fatto domanda di congedo per %(group)s."
|
||||
@ -1136,16 +1177,6 @@ msgstr "Crea una domanda"
|
||||
msgid "Username"
|
||||
msgstr "Nome utente"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Azioni"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1484,10 +1515,6 @@ msgstr "Modello"
|
||||
msgid "Code Name"
|
||||
msgstr "Nome del codice"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Utenti"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "Stati"
|
||||
@ -2233,14 +2260,12 @@ msgstr ""
|
||||
" "
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"%(queue_length)scompiti in coda"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
msgid "Admin"
|
||||
@ -2255,11 +2280,11 @@ msgid "AA Support Discord"
|
||||
msgstr "AA Discord di supporto"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "Menu utente"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "Disconnettersi"
|
||||
|
||||
@ -2315,22 +2340,30 @@ msgid "Objective"
|
||||
msgstr "Obiettivo"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "Giorni rimanenti"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "Ore rimanenti"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "Minuti rimanenti "
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "Importante"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "Limitato alla corporazione"
|
||||
|
||||
|
Binary file not shown.
@ -4,8 +4,8 @@
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Foch Petain <brigadier.rockforward@gmail.com>, 2023
|
||||
# kotaneko, 2023
|
||||
# Foch Petain <brigadier.rockforward@gmail.com>, 2023
|
||||
# Joel Falknau <ozirascal@gmail.com>, 2023
|
||||
#
|
||||
#, fuzzy
|
||||
@ -13,8 +13,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2023\n"
|
||||
"Language-Team: Japanese (https://app.transifex.com/alliance-auth/teams/107430/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -31,7 +31,7 @@ msgstr "Google ユニバーサル アナリティクス"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google アナリティクス 4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr "実行するためにはメインキャラクターの設定が必要です。設定してください。"
|
||||
|
||||
@ -44,63 +44,68 @@ msgstr "メールアドレス"
|
||||
msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr "これらの制限付きグループを追加または削除することはできません。%s"
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "英語"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "ドイツ語"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "スペイン語"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "中国語 簡体字"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "ロシア語"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "韓国語"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "フランス語"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "日本語"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "イタリア語"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "言語"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "ナイトモード"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "分類が%sに変更されました。"
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "あなたの分類は%(state)sになりました。"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "ダッシュボード"
|
||||
@ -158,8 +163,50 @@ msgstr "Corp"
|
||||
msgid "Alliance"
|
||||
msgstr "Alliance"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "アクション"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "キャラクター"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "ログイン"
|
||||
|
||||
@ -191,47 +238,49 @@ msgstr "登録"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "アクティベーションリンクが無効か期限切れです。"
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
"account."
|
||||
msgstr "メインキャラクターを%(char)sへ変更できません。別のアカウントによって利用されています。"
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "メインキャラクターを%(char)sへ変更しました。"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "%(name)sをアカウントに追加しました。"
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr "%(name)sをアカウントに追加することができません。すでに他のアカウントを持っています。"
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "選択されたキャラクターの認証が行えませんでした。"
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "Registrationトークンが有効期限切れです。"
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
msgstr "確認のメールを送信しました。メール内のリンクをご確認の上、メールアドレスの認証を完了させてください。"
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr "メールアドレスを確認しました。続行するにはログインしてください。"
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "新規アカウントの登録は、現時点ではできません。"
|
||||
|
||||
@ -274,19 +323,6 @@ msgstr "未登録"
|
||||
msgid "Last update:"
|
||||
msgstr "最終更新:"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "キャラクター"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -616,19 +652,24 @@ msgstr "{character.character_name} のフリート参加を登録できません
|
||||
msgid "Group Management"
|
||||
msgstr "グループ管理"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "ユーザ"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr "この名前は予約済みで、グループには使用できません。"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(auto)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "その名前のグループが既に存在しています。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -637,18 +678,18 @@ msgstr ""
|
||||
"内部グループです。ユーザーはこのグループを表示したり、参加したり、参加をリクエストしたりすることはできません。<br>Members、Corp_*、Alliance_*などのグループに使用します。選択すると、非表示"
|
||||
" オプションと 開く <br> <b> オプションが上書きされます。</b> "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr "グループはユーザーに表示されませんが、正しいリンク経由で参加することができます。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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 ""
|
||||
"グループはオープンで、ユーザーはリクエストに応じて自動的に追加されます。<br>グループがオープンでない場合、ユーザーのリクエストは手動で承認する必要があります。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -658,13 +699,13 @@ msgstr ""
|
||||
"グループは一般公開されています。登録ユーザーなら誰でもこのグループに参加でき、このグループに設定されている他のオプションに基づいて表示されます。<br>認証されなくなっても、Auth"
|
||||
" はユーザーをこのグループから自動的に削除しません。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
msgstr "グループは参加が制限されています。つまり、このグループのユーザーを追加または削除するには、スーパーユーザー管理者が必要です。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
msgid ""
|
||||
"Group leaders can process requests for this group. Use the "
|
||||
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||
@ -673,7 +714,7 @@ msgstr ""
|
||||
"グループリーダーは、このグループのリクエストを処理できます。<code>auth.group_management </code> "
|
||||
"権限を使用して、ユーザーがすべてのグループを管理できるようにします。<br> "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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 "
|
||||
@ -682,46 +723,46 @@ msgstr ""
|
||||
"リーダーグループのメンバーは、このグループのリクエストを処理できます。<code>auth.group_management </code> "
|
||||
"権限を使用して、ユーザーがすべてのグループを管理できるようにします。<br> "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
msgstr "ここに記載されているStatesは、適切な許可があれば、このグループに参加できます。<br> "
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr "ユーザーに表示されるグループの簡単な説明 <i> (最大 512 文字) </i>。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "非公開グループをリクエストできる"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "名前"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "グループには使用できない名前。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "理由"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "この名前が使用予約されている理由。"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "作成者"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "作成日時"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "このエントリが作成された日付"
|
||||
|
||||
@ -948,86 +989,86 @@ msgstr "グループリクエスト"
|
||||
msgid "Group Membership"
|
||||
msgstr "グループメンバーシップ"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "%(user)sを%(group)sから削除する。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "誰もグループに参加してません。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "グループが存在しません。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)sからの%(group)sへの参加申請を承認しました。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
"%(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)sからの%(group)sへの参加申請を処理中にエラーが発生しました。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)sからの%(group)sへの参加申請は拒否されました。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)sからの%(group)sからの脱退申請は承認されました。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
"%(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)sからの%(group)sからの脱退申請を処理中にエラーが発生しました。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)sの%(group)sからの脱退申請は拒否されました。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "このGroupには入れません"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "すでにその Group に参加してます。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "すでに参加申請を送付済みです。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "%(group)sへの参加申請を送信しました。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "この Group から脱退することはできません"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "あなたはその Group のメンバーではありません"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "すでに脱退申請を送信済みです。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "%(group)sからの脱退申請を送信しました。"
|
||||
@ -1089,16 +1130,6 @@ msgstr "申請を作成"
|
||||
msgid "Username"
|
||||
msgstr "ユーザー名"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "アクション"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1437,10 +1468,6 @@ msgstr "モデル"
|
||||
msgid "Code Name"
|
||||
msgstr "コードネーム"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "ユーザ"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "States"
|
||||
@ -2171,15 +2198,12 @@ msgstr ""
|
||||
" "
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
"\n"
|
||||
" %(queue_length)sキューに入っているタスク\n"
|
||||
" "
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
msgid "Admin"
|
||||
@ -2194,11 +2218,11 @@ msgid "AA Support Discord"
|
||||
msgstr "AA サポートディスコード"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "ユーザーメニュー"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "ログアウト"
|
||||
|
||||
@ -2254,22 +2278,30 @@ msgid "Objective"
|
||||
msgstr "目標"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "残り日数"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "残り時間"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "残り分数"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "重要"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "コーポレーション制限付き"
|
||||
|
||||
|
Binary file not shown.
@ -4,22 +4,22 @@
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# None None <khd1226543@gmail.com>, 2023
|
||||
# Joel Falknau <ozirascal@gmail.com>, 2023
|
||||
# Seowon Jung <seowon@hawaii.edu>, 2023
|
||||
# Olgeda Choi <undead.choi@gmail.com>, 2023
|
||||
# None None <khd1226543@gmail.com>, 2023
|
||||
# ThatRagingKid, 2023
|
||||
# Lahty <js03js70@gmail.com>, 2023
|
||||
# jackfrost, 2023
|
||||
# Olgeda Choi <undead.choi@gmail.com>, 2023
|
||||
# Seowon Jung <seowon@hawaii.edu>, 2023
|
||||
# Alpha, 2023
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"Last-Translator: jackfrost, 2023\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Alpha, 2023\n"
|
||||
"Language-Team: Korean (Korea) (https://app.transifex.com/alliance-auth/teams/107430/ko_KR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -35,7 +35,7 @@ msgstr "Google 애널리틱스 유니버설"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google 애널리틱스 V4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr "해당 기능을 수행하려면 주 캐릭터가 요구됩니다. 아래에서 하나를 추가하시오."
|
||||
|
||||
@ -48,63 +48,68 @@ msgstr "이메일"
|
||||
msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "영어"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "독일어"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "스페인어"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "간체자"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "러시아어"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "한국어"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "프랑스어"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "일본어"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "이탈리아어"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
msgid "Language"
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "야간 모드"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "상태가 %s로 변경됐습니다."
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "사용자의 상태는 %(state)s입니다."
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "대시보드"
|
||||
@ -163,8 +168,50 @@ msgstr "코퍼레이션"
|
||||
msgid "Alliance"
|
||||
msgstr "얼라이언스"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "활동"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "캐릭터"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "로그인"
|
||||
|
||||
@ -196,47 +243,49 @@ msgstr "등록"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "유효하지 않거나 만료된 활성화 주소"
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
"account."
|
||||
msgstr "%(char)s를 주 캐릭터로 변경할 수 없음: 다른 계정이 해당 캐릭터를 소유하고 있습니다."
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "주 캐릭터가 %(char)s로 변경됨"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "계정에 %(name)s를 추가했습니다."
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr "계정에 %(name)s를 추가하지 못했습니다. 이미 다른 계정에 추가되었습니다."
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "선택한 캐릭터로 인증할 수 없습니다."
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "가입 토큰이 만료되었습니다."
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
msgstr "확인 메일 전송됨. 다음 링크를 눌러 이메일 주소를 확인하세요."
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr "이메일 주소가 확인되었습니다. 로그인 해주세요."
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "현재 새로운 계정 등록은 받지않습니다."
|
||||
|
||||
@ -279,19 +328,6 @@ msgstr "미등록"
|
||||
msgid "Last update:"
|
||||
msgstr "마지막 업데이트:"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "캐릭터"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -621,19 +657,24 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "그룹 관리"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "사용자"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr "이 이름은 이미 사용되었으며 그룹의 이름으로 사용될 수 없습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(자동)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "이 이름을 가진 그룹이 이미 있습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -642,11 +683,11 @@ msgstr ""
|
||||
"시스템 그룹, 유저들은 이 그룹을 보거나, 참여하거나, 지원할 수 없습니다. <br>멤버, 코퍼레이션_*, 얼라이언스_* 등에 "
|
||||
"사용됨.<br><b>선택된 경우 비공개와 공개 옵션을 무시함.</b>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr "비공개 그룹이지만 링크를 통해 참여할 수 있음."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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."
|
||||
@ -654,7 +695,7 @@ msgstr ""
|
||||
"그룹은 공개되어 있으며 요청 시 유저는 자동적으로 추가됩니다.<br>그룹이 공개되어 있지 않은 경우, 유저는 직접 요청을 승인받아야 "
|
||||
"합니다."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -664,13 +705,13 @@ msgstr ""
|
||||
"공개 그룹입니다. 등록된 모든 유저는 이 그룹에 참여할 수 있으며, 이 그룹의 설정에 따라 공개 여부가 달라집니다.<br>유저가 더 "
|
||||
"이상 인증을 하지 않을 때, Auth는 이 그룹에서 유저를 자동 추방하지 않습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
msgid ""
|
||||
"Group leaders can process requests for this group. Use the "
|
||||
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||
@ -679,7 +720,7 @@ msgstr ""
|
||||
"그룹 리더는 이 그룹의 요청을 처리할 수 있습니다. <code>auth.group_management</code> 권한을 사용하여 "
|
||||
"사용자가 모든 그룹을 관리할 수 있도록 합니다.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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 "
|
||||
@ -688,46 +729,46 @@ msgstr ""
|
||||
"리더 그룹의 구성원은 이 그룹에 대한 요청을 처리할 수 있습니다. <code>1auth.group_management1</code> "
|
||||
"권한을 사용하여 사용자가 모든 그룹을 관리할 수 있도록 합니다.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
msgstr "만약 그들이 적절한 권한을 가졌다면, 여기 목록에 있는 신분 상태는 이 그룹에 가입할 수 있습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr "사용자에게 나타나는 그룹에 대한 간단한 설명 <i>(최대 512자)</i> 입니다."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "공용 그룹에 가입할 수 없음"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "이름"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "그룹에 사용할 수 없는 이름입니다."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "원인"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "이 이름이 예약된 이유입니다."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "생성자:"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "생성일:"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "이 항목이 생성된 날짜"
|
||||
|
||||
@ -954,86 +995,86 @@ msgstr "그룹 요청"
|
||||
msgid "Group Membership"
|
||||
msgstr "참가 중인 그룹"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "유저 %(user)s이(가) %(group)s에서 제거됨."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "사용자가 해당 그룹에 존재하지 않음."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "그룹이 존재하지 않음."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)s의 %(group)s 그룹 신청 수락"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
"%(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)s의 %(group)s 그룹 신청을 처리하는 중 알 수 없는 에러 발생"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)s의 %(group)s 그룹 신청 거절"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴 수락"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
"%(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴를 처리하는 중 알 수 없는 에러 발생"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴 거절"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "해당 그룹에 참여할 수 없습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "이미 해당 그룹에 가입되어 있습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "해당 그룹에 대한 참여신청이 보류되었습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "%(group)s그룹에 지원하였음."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "해당 그룹을 떠날 수 없습니다."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "해당그룹의 멤버가 아닙니다."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "해당 그룹의 탈퇴 신청이 접수된 상태입니다."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "%(group)s그룹의 탈퇴가 신청됨."
|
||||
@ -1095,16 +1136,6 @@ msgstr "지원서 작성"
|
||||
msgid "Username"
|
||||
msgstr "사용자명"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "활동"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1443,10 +1474,6 @@ msgstr "모델"
|
||||
msgid "Code Name"
|
||||
msgstr "코드명"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "사용자"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "상태"
|
||||
@ -2170,11 +2197,11 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
@ -2190,11 +2217,11 @@ msgid "AA Support Discord"
|
||||
msgstr "AA Support Discord"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "사용자 매뉴"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "로그아웃"
|
||||
|
||||
@ -2250,22 +2277,30 @@ msgid "Objective"
|
||||
msgstr "목표 대상"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "남은 일수"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "남은 시간"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "남은 분"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "중요"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "코퍼레이션 제한"
|
||||
|
||||
|
Binary file not shown.
@ -4,8 +4,8 @@
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Yuriy K <thedjcooltv@gmail.com>, 2023
|
||||
# Андрей Зубков <and.vareba81@gmail.com>, 2023
|
||||
# Yuriy K <thedjcooltv@gmail.com>, 2023
|
||||
# Alexander Gess <de.alex.gess@gmail.com>, 2023
|
||||
# Filipp Chertiev <f@fzfx.ru>, 2023
|
||||
#
|
||||
@ -14,8 +14,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Filipp Chertiev <f@fzfx.ru>, 2023\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/alliance-auth/teams/107430/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -32,7 +32,7 @@ msgstr "Google Analytics Universal"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google Analytics V4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr ""
|
||||
"Для продолжения следует указать основного персонажа. Выберите его ниже."
|
||||
@ -46,63 +46,68 @@ msgstr "Email"
|
||||
msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr "Вам не разрешено добавлять или удалять эти ограниченные группы: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "Английский"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "Немецкий"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "Испанский"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Китайский упрощённый"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "Русский"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "Корейский"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "Французский"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "Японский"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "Итальянский"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "Язык"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "Ночной режим"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "Статус изменен: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "Статус пилота: %(state)s"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "Панель показателей"
|
||||
@ -161,8 +166,50 @@ msgstr "Корпорация"
|
||||
msgid "Alliance"
|
||||
msgstr "Альянс"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Действия"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Персонаж"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "Вход"
|
||||
|
||||
@ -195,7 +242,7 @@ msgstr "Регистрация"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "Ссылка активации устарела"
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
@ -203,40 +250,42 @@ msgid ""
|
||||
msgstr ""
|
||||
"Нельзя сменить основного персонажа на %(char)s: похоже, что Владелец не Вы. "
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "Основной персонаж заменен на %(char)s"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "Добавлен %(name)s на Ваш аккаунт."
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr "Персонаж %(name)s уже добавлен."
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "Невозможно авторизировать этого персонажа. "
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "Регистрационный токен просрочен."
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
msgstr "Отправить подтверждающее письмо. Пожалуйста, подтвердите почту. "
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr "Подтвердите Ваш email адрес. Зайти для подтверждения. "
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "Регистрация новых аккаунтов в настоящее время невозможна."
|
||||
|
||||
@ -279,19 +328,6 @@ msgstr "Не зарегистрированы"
|
||||
msgid "Last update:"
|
||||
msgstr "Последнее обновление: "
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Персонаж"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -629,20 +665,25 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "Управление Группой"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Пользователи"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr ""
|
||||
"Это имя является зарезервированным и не может быть использовано для групп."
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(авто)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "Группа с таким именем уже существует."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -653,13 +694,13 @@ msgstr ""
|
||||
"Members, Corp_*, Alliance_* и т. п.<br><b>Будучи выбранной, отменяет "
|
||||
"настройки \"Скрытая\" и \"Открытая\".</b>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr ""
|
||||
"Группы скрыты от пользователей, но к ним всё ещё можно присоединиться с "
|
||||
"помощью корректной ссылки."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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."
|
||||
@ -668,7 +709,7 @@ msgstr ""
|
||||
"при отправке запроса.<br>Если группа не является открытой, запросы от "
|
||||
"пользователей будут требовать ручного подтверждения."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -680,7 +721,7 @@ msgstr ""
|
||||
"настройках данной группы.<br>Auth не будет удалять пользователей из этой "
|
||||
"группы автоматически при окончании срока их аутентификации."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
@ -688,7 +729,7 @@ msgstr ""
|
||||
"Группа является ограниченной. Это значит что добавление пользователей в эту "
|
||||
"группу или удаление из неё требует прав superuser admin."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
msgid ""
|
||||
"Group leaders can process requests for this group. Use the "
|
||||
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||
@ -698,7 +739,7 @@ msgstr ""
|
||||
"Используйте разрешение <code>auth.group_management</code>, чтобы позволить "
|
||||
"пользователю управлять всеми группами.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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 "
|
||||
@ -708,7 +749,7 @@ msgstr ""
|
||||
"Используйте разрешение <code>auth.group_management</code>, чтобы позволить "
|
||||
"пользователю управлять всеми группами.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
@ -716,42 +757,42 @@ msgstr ""
|
||||
"Статусы, перечисленные здесь, смогут присоединиться к группе, если у них "
|
||||
"есть соответствующие разрешения.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr ""
|
||||
"Краткое описание <i>(макс. 512 символов)</i> группы, отображаемое "
|
||||
"пользователям."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "Можно отправлять запрос на непубличную группу."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "имя"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "Имя, которое не может быть использовано для групп."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "причина"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "Причина, по которой это имя зарезервировано."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "создано кем"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "создано когда"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "Дата, когда данное содержимое было создано"
|
||||
|
||||
@ -978,26 +1019,26 @@ msgstr "Групповой запрос"
|
||||
msgid "Group Membership"
|
||||
msgstr "Групповое участие"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "Пользователь %(user)s исключен из %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "Пользователь не существует в этой группе."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "Группа не существует."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "Запрос от %(mainchar)sв %(group)s принят."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1006,18 +1047,18 @@ msgstr ""
|
||||
"Персонаж %(mainchar)s не может быть добавлен %(group)s, из-за непредвиденной"
|
||||
" ошибки. "
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)s исключен из %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "Утвержден выход %(mainchar)s из %(group)s. "
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1026,42 +1067,42 @@ msgstr ""
|
||||
"Возникла ошибка во время обработки %(mainchar)s на выход из группы "
|
||||
"%(group)s. Повторите позже."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "Прошение об исключении %(mainchar)s из %(group)s – отклонено. "
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "Вы не можете вступить"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "Вы уже участник этой группы."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "Вы уже подали заявку на вступление этой группы."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "Вступить в группу %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "Вы не можете покинуть эту группу"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "Вы не участник группыы"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "Ваш запрос находится на рассмотрении"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "Запрос на выход из группы %(group)s."
|
||||
@ -1123,16 +1164,6 @@ msgstr "Сделать запрос"
|
||||
msgid "Username"
|
||||
msgstr "Пользователь"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Действия"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1471,10 +1502,6 @@ msgstr "Модель"
|
||||
msgid "Code Name"
|
||||
msgstr "Кодовое имя"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Пользователи"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "Статусы"
|
||||
@ -2216,14 +2243,12 @@ msgstr ""
|
||||
" Статус %(total)s обработанных задач • последние %(latest)s"
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
"\n"
|
||||
" %(queue_length)s запланированных задач"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
msgid "Admin"
|
||||
@ -2238,11 +2263,11 @@ msgid "AA Support Discord"
|
||||
msgstr "Discord поддержки AA"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "Меню пользователя"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "Выход"
|
||||
|
||||
@ -2298,22 +2323,30 @@ msgid "Objective"
|
||||
msgstr "Задача"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "Дней осталось"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "Часов осталось"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "Минут осталось"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "Важно"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "Корпорация зарегистрированна"
|
||||
|
||||
|
Binary file not shown.
@ -12,8 +12,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Kristof Swensen, 2023\n"
|
||||
"Language-Team: Ukrainian (https://app.transifex.com/alliance-auth/teams/107430/uk/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -30,7 +30,7 @@ msgstr "Універсальна Google Аналітика"
|
||||
msgid "Google Analytics V4"
|
||||
msgstr "Google Analytics V4"
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr ""
|
||||
"Для виконання цієї дії потрібен основний персонаж. Додайте його нижче."
|
||||
@ -44,63 +44,68 @@ msgstr "Електронна пошта"
|
||||
msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr "Вам заборонено додавати або видаляти ці обмежені групи: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "Англійська"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "Німецька"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "Іспанська"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Китайська спрощена"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "Російська"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "Корейська"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "Французька"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "Японська"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "Італійська"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "Мова"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "Нічний режим"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr "Стан змінено на: %s"
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr "Стан вашого користувача зараз: %(state)s"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "Панель приладів"
|
||||
@ -158,8 +163,50 @@ msgstr "Корпорація"
|
||||
msgid "Alliance"
|
||||
msgstr "Альянс"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Дії"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Персонаж"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "Увійти"
|
||||
|
||||
@ -193,7 +240,7 @@ msgstr "Зареєструватися"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "Невірне або прострочене посилання для активації."
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
@ -202,32 +249,34 @@ msgstr ""
|
||||
"Неможливо змінити основного персонажа на %(char)s: персонаж належить іншому "
|
||||
"акаунту."
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "Основний персонаж змінено на %(char)s"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "Додано %(name)s до вашого облікового запису."
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr ""
|
||||
"Не вдалося додати %(name)s до вашого облікового запису: у них вже є "
|
||||
"обліковий запис."
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "Не вдалося автентифікуватися як обраний персонаж."
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "Токен реєстрації застарів."
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
@ -235,13 +284,13 @@ msgstr ""
|
||||
"Відправлено лист з підтвердженням. Будь ласка, перейдіть за посиланням, щоб "
|
||||
"підтвердити свою адресу електронної пошти."
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr ""
|
||||
"Підтверджено вашу адресу електронної пошти. Будь ласка, увійдіть, щоб "
|
||||
"продовжити."
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr "Реєстрація нових облікових записів наразі не дозволена."
|
||||
|
||||
@ -284,19 +333,6 @@ msgstr "Незареєстровані"
|
||||
msgid "Last update:"
|
||||
msgstr "Останнє оновлення:"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "Персонаж"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -634,19 +670,24 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "Керування групами"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Користувачі"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr "Це ім'я зарезервоване і не може бути використане для груп."
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr "(авто)"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr "Група з таким ім'ям вже існує."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
msgid ""
|
||||
"Internal group, users cannot see, join or request to join this "
|
||||
"group.<br>Used for groups such as Members, Corp_*, Alliance_* "
|
||||
@ -657,12 +698,12 @@ msgstr ""
|
||||
"\"Members, Corp_, Alliance_ і т.д.<br><b>Перевизначає параметри Hidden і \"\n"
|
||||
"\"Open при виборі.</b>\""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr ""
|
||||
"Група прихована від користувачів, але можна приєднатися за посиланням."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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."
|
||||
@ -671,7 +712,7 @@ msgstr ""
|
||||
"запитом.<br>Якщо група закрита, користувачі повинні отримати ручне "
|
||||
"підтвердження запиту."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -683,7 +724,7 @@ msgstr ""
|
||||
"групи.<br>Авторизація не буде автоматично видаляти користувачів з цієї "
|
||||
"групи, коли вони більше не автентифіковані."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
@ -691,7 +732,7 @@ msgstr ""
|
||||
"Група обмежена. Це означає, що додавання або видалення користувачів для цієї"
|
||||
" групи вимагає адміністратора-суперкористувача."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
msgid ""
|
||||
"Group leaders can process requests for this group. Use the "
|
||||
"<code>auth.group_management</code> permission to allow a user to manage all "
|
||||
@ -701,7 +742,7 @@ msgstr ""
|
||||
"<code>auth.group_management</code>, щоб дозволити користувачеві керувати "
|
||||
"всіма групами.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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 "
|
||||
@ -711,7 +752,7 @@ msgstr ""
|
||||
" дозвіл <code>auth.group_management</code>, щоб дозволити користувачеві "
|
||||
"керувати всіма групами.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
@ -719,42 +760,42 @@ msgstr ""
|
||||
"Штати, перераховані тут, матимуть змогу приєднатися до цієї групи, якщо вони"
|
||||
" мають відповідні дозволи.<br>"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr ""
|
||||
"Короткий опис <i>(максимум 512 символів)</i> групи, що відображається "
|
||||
"користувачам."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr "Може запитувати непублічні групи"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr "назва"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr "Назва, яку неможна використовувати для груп."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "причина"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr "Причина, чому ця назва зарезервована."
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr "створено користувачем"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr "створено о"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr "Дата створення цього запису"
|
||||
|
||||
@ -981,26 +1022,26 @@ msgstr "Групові запити"
|
||||
msgid "Group Membership"
|
||||
msgstr "Членство в групі"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "Користувач %(user)s вилучений з групи %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "Користувача не існує в цій групі"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "Група не існує"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "Заявка %(mainchar)s на вступ до %(group)s прийнята."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1009,18 +1050,18 @@ msgstr ""
|
||||
"Під час обробки заявки %(mainchar)s на вступ до %(group)s виникла помилка, "
|
||||
"яку не можна обробити."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "Заявка %(mainchar)s на вступ до %(group)s відхилена."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "Заявка %(mainchar)s на вихід з %(group)s прийнята."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
@ -1029,42 +1070,42 @@ msgstr ""
|
||||
"Під час обробки заявки %(mainchar)s на вихід з %(group)s виникла помилка, "
|
||||
"яку не можна обробити."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "Заявка %(mainchar)s на вихід з %(group)s відхилена."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "Ви не можете приєднатись до цієї групи"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "Ви вже є членом цієї групи."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "Ви вже подали заявку на вступ до цієї групи."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "Подано заявку на групу %(group)s."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "Ви не можете покинути цю групу"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "Ви не є учасником цієї групи"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "Ви вже подали запит на вихід з цієї групи."
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "Подано заявку на вихід з групи %(group)s."
|
||||
@ -1126,16 +1167,6 @@ msgstr "Створити заявку"
|
||||
msgid "Username"
|
||||
msgstr "Ім'я користувача"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "Дії"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1474,10 +1505,6 @@ msgstr "Модель"
|
||||
msgid "Code Name"
|
||||
msgstr "Кодова назва"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "Користувачі"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "Стани"
|
||||
@ -2220,14 +2247,12 @@ msgstr ""
|
||||
"Статус %(total)s виконаних завдань • останній %(latest)s"
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"%(queue_length)s завдань в черзі"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
msgid "Admin"
|
||||
@ -2242,11 +2267,11 @@ msgid "AA Support Discord"
|
||||
msgstr "Підтримка AA у Discord"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr "Меню Користувача"
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "Вихід"
|
||||
|
||||
@ -2302,22 +2327,30 @@ msgid "Objective"
|
||||
msgstr "Мета"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "Залишилося днів"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "Залишилося годин"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "Залишилося хвилин"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "Важливо"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "Обмежено для корпорації"
|
||||
|
||||
|
Binary file not shown.
@ -4,19 +4,19 @@
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Shen Yang, 2023
|
||||
# Jesse . <sgeine@hotmail.com>, 2023
|
||||
# Aaron BuBu <351793078@qq.com>, 2023
|
||||
# Joel Falknau <ozirascal@gmail.com>, 2023
|
||||
# Shen Yang, 2023
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-09 18:20+1000\n"
|
||||
"PO-Revision-Date: 2023-10-08 09:23+0000\n"
|
||||
"Last-Translator: Shen Yang, 2023\n"
|
||||
"POT-Creation-Date: 2024-02-17 18:50+1000\n"
|
||||
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
|
||||
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2023\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -32,7 +32,7 @@ msgstr ""
|
||||
msgid "Google Analytics V4"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/decorators.py:37
|
||||
#: allianceauth/authentication/decorators.py:49
|
||||
msgid "A main character is required to perform that action. Add one below."
|
||||
msgstr "只有主要角色才能执行这个操作。在下面添加一个"
|
||||
|
||||
@ -45,63 +45,68 @@ msgstr "电子邮箱"
|
||||
msgid "You are not allowed to add or remove these restricted groups: %s"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:80
|
||||
#: allianceauth/authentication/models.py:71
|
||||
msgid "English"
|
||||
msgstr "英语"
|
||||
|
||||
#: allianceauth/authentication/models.py:81
|
||||
#: allianceauth/authentication/models.py:72
|
||||
msgid "German"
|
||||
msgstr "德语"
|
||||
|
||||
#: allianceauth/authentication/models.py:82
|
||||
#: allianceauth/authentication/models.py:73
|
||||
msgid "Spanish"
|
||||
msgstr "西班牙语"
|
||||
|
||||
#: allianceauth/authentication/models.py:83
|
||||
#: allianceauth/authentication/models.py:74
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "简体中文"
|
||||
|
||||
#: allianceauth/authentication/models.py:84
|
||||
#: allianceauth/authentication/models.py:75
|
||||
msgid "Russian"
|
||||
msgstr "俄语"
|
||||
|
||||
#: allianceauth/authentication/models.py:85
|
||||
#: allianceauth/authentication/models.py:76
|
||||
msgid "Korean"
|
||||
msgstr "韩语"
|
||||
|
||||
#: allianceauth/authentication/models.py:86
|
||||
#: allianceauth/authentication/models.py:77
|
||||
msgid "French"
|
||||
msgstr "法语"
|
||||
|
||||
#: allianceauth/authentication/models.py:87
|
||||
#: allianceauth/authentication/models.py:78
|
||||
msgid "Japanese"
|
||||
msgstr "日语"
|
||||
|
||||
#: allianceauth/authentication/models.py:88
|
||||
#: allianceauth/authentication/models.py:79
|
||||
msgid "Italian"
|
||||
msgstr "意大利语"
|
||||
|
||||
#: allianceauth/authentication/models.py:91
|
||||
#: allianceauth/authentication/models.py:80
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
msgid "Language"
|
||||
msgstr "语言"
|
||||
|
||||
#: allianceauth/authentication/models.py:96
|
||||
#: allianceauth/authentication/models.py:101
|
||||
#: allianceauth/templates/allianceauth/night-toggle.html:6
|
||||
msgid "Night Mode"
|
||||
msgstr "夜间模式"
|
||||
|
||||
#: allianceauth/authentication/models.py:110
|
||||
#: allianceauth/authentication/models.py:115
|
||||
#, python-format
|
||||
msgid "State changed to: %s"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/models.py:111
|
||||
#: allianceauth/authentication/models.py:116
|
||||
#, python-format
|
||||
msgid "Your user's state is now: %(state)s"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:4
|
||||
#: allianceauth/authentication/templates/authentication/dashboard.html:7
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:4
|
||||
#: allianceauth/templates/allianceauth/side-menu.html:10
|
||||
msgid "Dashboard"
|
||||
msgstr "账户总览"
|
||||
@ -157,8 +162,50 @@ msgstr "所在公司"
|
||||
msgid "Alliance"
|
||||
msgstr "所在联盟"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:7
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
|
||||
msgid "Token Management"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:12
|
||||
msgid "Scopes"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:13
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "操作"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:14
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "角色"
|
||||
|
||||
#: allianceauth/authentication/templates/authentication/tokens.html:28
|
||||
msgid ""
|
||||
"This page is a best attempt, but backups or database logs can still contain "
|
||||
"your tokens. Always revoke tokens on "
|
||||
"https://community.eveonline.com/support/third-party-applications/ where "
|
||||
"possible."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/templates/public/login.html:6
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
|
||||
msgid "Login"
|
||||
msgstr "登录"
|
||||
|
||||
@ -190,47 +237,49 @@ msgstr "注册"
|
||||
msgid "Invalid or expired activation link."
|
||||
msgstr "激活链接无效或过期"
|
||||
|
||||
#: allianceauth/authentication/views.py:77
|
||||
#: allianceauth/authentication/views.py:118
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot change main character to %(char)s: character owned by a different "
|
||||
"account."
|
||||
msgstr "不能修改主角色为%(char)s:这个角色被另一个账户所拥有"
|
||||
|
||||
#: allianceauth/authentication/views.py:83
|
||||
#: allianceauth/authentication/views.py:124
|
||||
#, python-format
|
||||
msgid "Changed main character to %(char)s"
|
||||
msgstr "修改主要角色为%(char)s"
|
||||
|
||||
#: allianceauth/authentication/views.py:92
|
||||
#: allianceauth/authentication/views.py:133
|
||||
#, python-format
|
||||
msgid "Added %(name)s to your account."
|
||||
msgstr "添加%(name)s到您的账户"
|
||||
|
||||
#: allianceauth/authentication/views.py:94
|
||||
#: allianceauth/authentication/views.py:135
|
||||
#, python-format
|
||||
msgid "Failed to add %(name)s to your account: they already have an account."
|
||||
msgstr "添加%(name)s到您的账户失败:他们已经在一个账户中了"
|
||||
|
||||
#: allianceauth/authentication/views.py:133
|
||||
msgid "Unable to authenticate as the selected character."
|
||||
msgstr "无法作为选定的角色进行身份验证"
|
||||
#: allianceauth/authentication/views.py:178
|
||||
msgid ""
|
||||
"Unable to authenticate as the selected character. Please log in with the "
|
||||
"main character associated with this account."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/authentication/views.py:197
|
||||
#: allianceauth/authentication/views.py:244
|
||||
msgid "Registration token has expired."
|
||||
msgstr "注册令牌过期。"
|
||||
|
||||
#: allianceauth/authentication/views.py:252
|
||||
#: allianceauth/authentication/views.py:302
|
||||
msgid ""
|
||||
"Sent confirmation email. Please follow the link to confirm your email "
|
||||
"address."
|
||||
msgstr "已经发送了确认邮件。请按照链接确定您的电邮地址"
|
||||
|
||||
#: allianceauth/authentication/views.py:257
|
||||
#: allianceauth/authentication/views.py:307
|
||||
msgid "Confirmed your email address. Please login to continue."
|
||||
msgstr "已确认您的电邮地址。请登录以继续"
|
||||
|
||||
#: allianceauth/authentication/views.py:262
|
||||
#: allianceauth/authentication/views.py:312
|
||||
msgid "Registration of new accounts is not allowed at this time."
|
||||
msgstr ""
|
||||
|
||||
@ -273,19 +322,6 @@ msgstr "未注册成员"
|
||||
msgid "Last update:"
|
||||
msgstr "最后一次更新"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:74
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:112
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:156
|
||||
#: allianceauth/corputils/templates/corputils/search.html:13
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
|
||||
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
|
||||
msgid "Character"
|
||||
msgstr "角色"
|
||||
|
||||
#: allianceauth/corputils/templates/corputils/corpstats.html:75
|
||||
#: allianceauth/corputils/templates/corputils/search.html:14
|
||||
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
|
||||
@ -615,36 +651,41 @@ msgstr ""
|
||||
msgid "Group Management"
|
||||
msgstr "用户组管理"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:15
|
||||
#: allianceauth/groupmanagement/forms.py:18
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "用户"
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:52
|
||||
msgid "This name has been reserved and can not be used for groups."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:25
|
||||
#: allianceauth/groupmanagement/forms.py:62
|
||||
msgid "(auto)"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/forms.py:34
|
||||
#: allianceauth/groupmanagement/forms.py:71
|
||||
msgid "There already exists a group with that name."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:105
|
||||
#: allianceauth/groupmanagement/models.py:104
|
||||
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:113
|
||||
#: allianceauth/groupmanagement/models.py:112
|
||||
msgid "Group is hidden from users but can still join with the correct link."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:119
|
||||
#: allianceauth/groupmanagement/models.py:118
|
||||
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:126
|
||||
#: allianceauth/groupmanagement/models.py:125
|
||||
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 "
|
||||
@ -652,66 +693,66 @@ msgid ""
|
||||
"authenticated."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:135
|
||||
#: allianceauth/groupmanagement/models.py:134
|
||||
msgid ""
|
||||
"Group is restricted. This means that adding or removing users for this group"
|
||||
" requires a superuser admin."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:144
|
||||
#: allianceauth/groupmanagement/models.py:143
|
||||
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:154
|
||||
#: allianceauth/groupmanagement/models.py:153
|
||||
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:163
|
||||
#: allianceauth/groupmanagement/models.py:162
|
||||
msgid ""
|
||||
"States listed here will have the ability to join this group provided they "
|
||||
"have the proper permissions.<br>"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:171
|
||||
#: allianceauth/groupmanagement/models.py:170
|
||||
msgid ""
|
||||
"Short description <i>(max. 512 characters)</i> of the group shown to users."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:178
|
||||
#: allianceauth/groupmanagement/models.py:177
|
||||
msgid "Can request non-public groups"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:209
|
||||
#: allianceauth/groupmanagement/models.py:208
|
||||
msgid "name"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:212
|
||||
#: allianceauth/groupmanagement/models.py:211
|
||||
msgid "Name that can not be used for groups."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "reason"
|
||||
msgstr "原因"
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:215
|
||||
#: allianceauth/groupmanagement/models.py:214
|
||||
msgid "Reason why this name is reserved."
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:218
|
||||
#: allianceauth/groupmanagement/models.py:217
|
||||
msgid "created by"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "created at"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/groupmanagement/models.py:223
|
||||
#: allianceauth/groupmanagement/models.py:222
|
||||
msgid "Date when this entry was created"
|
||||
msgstr ""
|
||||
|
||||
@ -938,86 +979,86 @@ msgstr "用户组请求"
|
||||
msgid "Group Membership"
|
||||
msgstr "用户组成员"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:163
|
||||
#: allianceauth/groupmanagement/views.py:166
|
||||
#, python-format
|
||||
msgid "Removed user %(user)s from group %(group)s."
|
||||
msgstr "已将用户%(user)s从用户组%(group)s中移除"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:165
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
msgid "User does not exist in that group"
|
||||
msgstr "那个用户组中不存在这个用户"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:168
|
||||
#: allianceauth/groupmanagement/views.py:171
|
||||
msgid "Group does not exist"
|
||||
msgstr "用户组不存在"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:195
|
||||
#: allianceauth/groupmanagement/views.py:198
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to %(group)s."
|
||||
msgstr "已接受用户%(mainchar)s加入%(group)s的申请"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:201
|
||||
#: allianceauth/groupmanagement/views.py:232
|
||||
#: allianceauth/groupmanagement/views.py:204
|
||||
#: allianceauth/groupmanagement/views.py:235
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
"%(mainchar)s to %(group)s."
|
||||
msgstr "在处理用户%(mainchar)s加入%(group)s的申请的过程中出现了一个搞不定的错误"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:226
|
||||
#: allianceauth/groupmanagement/views.py:229
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to %(group)s."
|
||||
msgstr "%(mainchar)s加入%(group)s的申请已拒绝"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:261
|
||||
#: allianceauth/groupmanagement/views.py:264
|
||||
#, python-format
|
||||
msgid "Accepted application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)s加入%(group)s的申请已通过"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:266
|
||||
#: allianceauth/groupmanagement/views.py:298
|
||||
#: allianceauth/groupmanagement/views.py:269
|
||||
#: allianceauth/groupmanagement/views.py:301
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An unhandled error occurred while processing the application from "
|
||||
"%(mainchar)s to leave %(group)s."
|
||||
msgstr "在处理%(mainchar)s离开%(group)s的程序时发生了未知的错误"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:292
|
||||
#: allianceauth/groupmanagement/views.py:295
|
||||
#, python-format
|
||||
msgid "Rejected application from %(mainchar)s to leave %(group)s."
|
||||
msgstr "%(mainchar)s离开%(group)s的请求已被拒绝"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:336
|
||||
#: allianceauth/groupmanagement/views.py:346
|
||||
#: allianceauth/groupmanagement/views.py:339
|
||||
#: allianceauth/groupmanagement/views.py:349
|
||||
msgid "You cannot join that group"
|
||||
msgstr "你无法加入那个用户组"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:341
|
||||
#: allianceauth/groupmanagement/views.py:344
|
||||
msgid "You are already a member of that group."
|
||||
msgstr "你已经是那个群组的一员了。"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:358
|
||||
#: allianceauth/groupmanagement/views.py:361
|
||||
msgid "You already have a pending application for that group."
|
||||
msgstr "你已经有了该组的未决申请"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:367
|
||||
#: allianceauth/groupmanagement/views.py:370
|
||||
#, python-format
|
||||
msgid "Applied to group %(group)s."
|
||||
msgstr "修改已经应用到%(group)s啦"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:377
|
||||
#: allianceauth/groupmanagement/views.py:380
|
||||
msgid "You cannot leave that group"
|
||||
msgstr "你无法离开那个用户组"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:381
|
||||
#: allianceauth/groupmanagement/views.py:384
|
||||
msgid "You are not a member of that group"
|
||||
msgstr "你不是那个用户组的成员"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:393
|
||||
#: allianceauth/groupmanagement/views.py:396
|
||||
msgid "You already have a pending leave request for that group."
|
||||
msgstr "你已经有了该组的未决离开请求"
|
||||
|
||||
#: allianceauth/groupmanagement/views.py:409
|
||||
#: allianceauth/groupmanagement/views.py:412
|
||||
#, python-format
|
||||
msgid "Applied to leave group %(group)s."
|
||||
msgstr "已经离开群组%(group)s"
|
||||
@ -1079,16 +1120,6 @@ msgstr "创建申请"
|
||||
msgid "Username"
|
||||
msgstr "用户名"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
|
||||
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
|
||||
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
|
||||
#: allianceauth/srp/templates/srp/data.html:101
|
||||
#: allianceauth/srp/templates/srp/management.html:44
|
||||
msgid "Actions"
|
||||
msgstr "操作"
|
||||
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:38
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
|
||||
#: allianceauth/hrapplications/templates/hrapplications/management.html:143
|
||||
@ -1427,10 +1458,6 @@ msgstr "类型"
|
||||
msgid "Code Name"
|
||||
msgstr "操作类型"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
|
||||
msgid "Users"
|
||||
msgstr "用户"
|
||||
|
||||
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
|
||||
msgid "States"
|
||||
msgstr "声望"
|
||||
@ -2153,11 +2180,11 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" %(queue_length)s queued tasks\n"
|
||||
" "
|
||||
msgid "running"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/admin-status/overview.html:96
|
||||
msgid "queued"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
|
||||
@ -2173,11 +2200,11 @@ msgid "AA Support Discord"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
|
||||
msgid "User Menu"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56
|
||||
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
|
||||
msgid "Logout"
|
||||
msgstr "登出"
|
||||
|
||||
@ -2233,22 +2260,30 @@ msgid "Objective"
|
||||
msgstr "声望"
|
||||
|
||||
#: allianceauth/timerboard/form.py:64
|
||||
msgid "Absolute Timer"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
msgid "Date and Time"
|
||||
msgstr ""
|
||||
|
||||
#: allianceauth/timerboard/form.py:66
|
||||
msgid "Days Remaining"
|
||||
msgstr "剩余天数"
|
||||
|
||||
#: allianceauth/timerboard/form.py:65
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
msgid "Hours Remaining"
|
||||
msgstr "剩余小时数"
|
||||
|
||||
#: allianceauth/timerboard/form.py:67
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
msgid "Minutes Remaining"
|
||||
msgstr "剩余分钟"
|
||||
|
||||
#: allianceauth/timerboard/form.py:69
|
||||
#: allianceauth/timerboard/form.py:71
|
||||
msgid "Important"
|
||||
msgstr "重要信息"
|
||||
|
||||
#: allianceauth/timerboard/form.py:70
|
||||
#: allianceauth/timerboard/form.py:72
|
||||
msgid "Corp-Restricted"
|
||||
msgstr "受限制的公司"
|
||||
|
||||
|
@ -1,9 +1,111 @@
|
||||
"""Admin site for menu app."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib import admin
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from . import models
|
||||
from .constants import MenuItemType
|
||||
from .core.smart_sync import sync_menu
|
||||
from .filters import MenuItemTypeListFilter
|
||||
from .forms import (
|
||||
AppMenuItemAdminForm,
|
||||
FolderMenuItemAdminForm,
|
||||
LinkMenuItemAdminForm,
|
||||
)
|
||||
from .models import MenuItem
|
||||
|
||||
|
||||
@admin.register(models.MenuItem)
|
||||
@admin.register(MenuItem)
|
||||
class MenuItemAdmin(admin.ModelAdmin):
|
||||
list_display = ['text', 'hide', 'parent', 'url', 'icon_classes', 'rank']
|
||||
ordering = ('rank',)
|
||||
list_display = (
|
||||
"_text",
|
||||
"parent",
|
||||
"order",
|
||||
"_user_defined",
|
||||
"_visible",
|
||||
"_children",
|
||||
)
|
||||
list_filter = [
|
||||
MenuItemTypeListFilter,
|
||||
"is_hidden",
|
||||
("parent", admin.RelatedOnlyFieldListFilter),
|
||||
]
|
||||
ordering = ["parent", "order", "text"]
|
||||
|
||||
def get_form(self, request: HttpRequest, obj: Optional[MenuItem] = None, **kwargs):
|
||||
kwargs["form"] = self._choose_form(request, obj)
|
||||
return super().get_form(request, obj, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def _choose_form(cls, request: HttpRequest, obj: Optional[MenuItem]):
|
||||
"""Return the form for the current menu item type."""
|
||||
if obj: # change
|
||||
if obj.hook_hash:
|
||||
return AppMenuItemAdminForm
|
||||
|
||||
if obj.is_folder:
|
||||
return FolderMenuItemAdminForm
|
||||
|
||||
return LinkMenuItemAdminForm
|
||||
|
||||
# add
|
||||
if cls._type_from_request(request) is MenuItemType.FOLDER:
|
||||
return FolderMenuItemAdminForm
|
||||
|
||||
return LinkMenuItemAdminForm
|
||||
|
||||
def add_view(self, request, form_url="", extra_context=None) -> HttpResponse:
|
||||
context = extra_context or {}
|
||||
item_type = self._type_from_request(request, default=MenuItemType.LINK)
|
||||
context["title"] = _("Add %s menu item") % item_type.label
|
||||
return super().add_view(request, form_url, context)
|
||||
|
||||
def change_view(
|
||||
self, request, object_id, form_url="", extra_context=None
|
||||
) -> HttpResponse:
|
||||
extra_context = extra_context or {}
|
||||
obj = get_object_or_404(MenuItem, id=object_id)
|
||||
extra_context["title"] = _("Change %s menu item") % obj.item_type.label
|
||||
return super().change_view(request, object_id, form_url, extra_context)
|
||||
|
||||
def changelist_view(self, request: HttpRequest, extra_context=None):
|
||||
# needed to ensure items are updated after an app change
|
||||
# and when the admin page is opened directly
|
||||
sync_menu()
|
||||
extra_context = extra_context or {}
|
||||
extra_context["folder_type"] = MenuItemType.FOLDER.value
|
||||
return super().changelist_view(request, extra_context)
|
||||
|
||||
@admin.display(description=_("children"))
|
||||
def _children(self, obj: MenuItem):
|
||||
if not obj.is_folder:
|
||||
return []
|
||||
|
||||
names = [obj.text for obj in obj.children.order_by("order", "text")]
|
||||
return names if names else "?"
|
||||
|
||||
@admin.display(description=_("text"), ordering="text")
|
||||
def _text(self, obj: MenuItem) -> str:
|
||||
if obj.is_folder:
|
||||
return f"[{obj.text}]"
|
||||
return obj.text
|
||||
|
||||
@admin.display(description=_("user defined"), boolean=True)
|
||||
def _user_defined(self, obj: MenuItem) -> bool:
|
||||
return obj.is_user_defined
|
||||
|
||||
@admin.display(description=_("visible"), ordering="is_hidden", boolean=True)
|
||||
def _visible(self, obj: MenuItem) -> bool:
|
||||
return not bool(obj.is_hidden)
|
||||
|
||||
@staticmethod
|
||||
def _type_from_request(
|
||||
request: HttpRequest, default=None
|
||||
) -> Optional[MenuItemType]:
|
||||
try:
|
||||
return MenuItemType(request.GET.get("type"))
|
||||
except ValueError:
|
||||
return default
|
||||
|
@ -1,19 +1,19 @@
|
||||
import logging
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.utils import ProgrammingError, OperationalError
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# TODO discuss permissions for user defined links
|
||||
# TODO define aa way for hooks to predefine a "parent" to create a sub menu from modules
|
||||
# TODO Add user documentation
|
||||
|
||||
|
||||
class MenuConfig(AppConfig):
|
||||
name = "allianceauth.menu"
|
||||
label = "menu"
|
||||
|
||||
def ready(self):
|
||||
try:
|
||||
from allianceauth.menu.providers import menu_provider
|
||||
menu_provider.clear_synced_flag()
|
||||
except (ProgrammingError, OperationalError):
|
||||
logger.warning("Migrations not completed for MenuItems")
|
||||
from allianceauth.menu.core import smart_sync
|
||||
|
||||
smart_sync.reset_menu_items_sync()
|
||||
|
18
allianceauth/menu/constants.py
Normal file
18
allianceauth/menu/constants.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""Global constants for the menu app."""
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
DEFAULT_FOLDER_ICON_CLASSES = "fa-solid fa-folder" # TODO: Make this a setting?
|
||||
"""Default icon class for folders."""
|
||||
|
||||
DEFAULT_MENU_ITEM_ORDER = 9999
|
||||
"""Default order for any menu item."""
|
||||
|
||||
|
||||
class MenuItemType(models.TextChoices):
|
||||
"""The type of a menu item."""
|
||||
|
||||
APP = "app", _("app")
|
||||
FOLDER = "folder", _("folder")
|
||||
LINK = "link", _("link")
|
48
allianceauth/menu/core/menu_item_hooks.py
Normal file
48
allianceauth/menu/core/menu_item_hooks.py
Normal file
@ -0,0 +1,48 @@
|
||||
"""Logic for handling MenuItemHook objects."""
|
||||
|
||||
import hashlib
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
from allianceauth.menu.hooks import MenuItemHook
|
||||
|
||||
|
||||
class MenuItemHookCustom(MenuItemHook):
|
||||
"""A user defined menu item that can be rendered with the standard template."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
text: str,
|
||||
classes: str,
|
||||
url_name: str,
|
||||
order: Optional[int] = None,
|
||||
navactive: Optional[List[str]] = None,
|
||||
):
|
||||
super().__init__(text, classes, url_name, order, navactive)
|
||||
self.url = ""
|
||||
self.is_folder = None
|
||||
self.html_id = ""
|
||||
self.children = []
|
||||
|
||||
|
||||
class MenuItemHookParams(NamedTuple):
|
||||
"""Immutable container for params about a menu item hook."""
|
||||
|
||||
text: str
|
||||
order: int
|
||||
hash: str
|
||||
|
||||
|
||||
def generate_hash(obj: MenuItemHook) -> str:
|
||||
"""Return the hash for a menu item hook."""
|
||||
my_class = obj.__class__
|
||||
name = f"{my_class.__module__}.{my_class.__name__}"
|
||||
hash_value = hashlib.sha256(name.encode("utf-8")).hexdigest()
|
||||
return hash_value
|
||||
|
||||
|
||||
def gather_params(obj: MenuItemHook) -> MenuItemHookParams:
|
||||
"""Return params from a menu item hook."""
|
||||
text = getattr(obj, "text", obj.__class__.__name__)
|
||||
order = getattr(obj, "order", None)
|
||||
hash = generate_hash(obj)
|
||||
return MenuItemHookParams(text=text, hash=hash, order=order)
|
30
allianceauth/menu/core/smart_sync.py
Normal file
30
allianceauth/menu/core/smart_sync.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""Provide capability to sync menu items when needed only."""
|
||||
|
||||
from django.core.cache import cache
|
||||
|
||||
_MENU_SYNC_CACHE_KEY = "ALLIANCEAUTH-MENU-SYNCED"
|
||||
|
||||
|
||||
def sync_menu() -> None:
|
||||
"""Sync menu items if needed only."""
|
||||
from allianceauth.menu.models import MenuItem
|
||||
|
||||
is_sync_needed = not _is_menu_synced() or not MenuItem.objects.exists()
|
||||
# need to also check for existence of MenuItems in database
|
||||
# to ensure the menu is synced during tests
|
||||
if is_sync_needed:
|
||||
MenuItem.objects.sync_all()
|
||||
_record_menu_was_synced()
|
||||
|
||||
|
||||
def _is_menu_synced() -> bool:
|
||||
return cache.get(_MENU_SYNC_CACHE_KEY, False)
|
||||
|
||||
|
||||
def _record_menu_was_synced() -> None:
|
||||
cache.set(_MENU_SYNC_CACHE_KEY, True, timeout=None) # no timeout
|
||||
|
||||
|
||||
def reset_menu_items_sync() -> None:
|
||||
"""Ensure menu items are synced, e.g. after a Django restart."""
|
||||
cache.delete(_MENU_SYNC_CACHE_KEY)
|
24
allianceauth/menu/filters.py
Normal file
24
allianceauth/menu/filters.py
Normal file
@ -0,0 +1,24 @@
|
||||
"""Filters for the menu app."""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from allianceauth.menu.constants import MenuItemType
|
||||
|
||||
|
||||
class MenuItemTypeListFilter(admin.SimpleListFilter):
|
||||
"""Allow filtering admin changelist by menu item type."""
|
||||
|
||||
title = _("type")
|
||||
parameter_name = "type"
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
return [(obj.value, obj.label.title()) for obj in MenuItemType]
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
if value := self.value():
|
||||
return queryset.annotate_item_type_2().filter(
|
||||
item_type_2=MenuItemType(value).value
|
||||
)
|
||||
|
||||
return None
|
49
allianceauth/menu/forms.py
Normal file
49
allianceauth/menu/forms.py
Normal file
@ -0,0 +1,49 @@
|
||||
from django import forms
|
||||
|
||||
from .constants import DEFAULT_FOLDER_ICON_CLASSES
|
||||
from .models import MenuItem
|
||||
|
||||
|
||||
class FolderMenuItemAdminForm(forms.ModelForm):
|
||||
"""A form for changing folder items."""
|
||||
|
||||
class Meta:
|
||||
model = MenuItem
|
||||
fields = ["text", "classes", "order", "is_hidden"]
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
if not data["classes"]:
|
||||
data["classes"] = DEFAULT_FOLDER_ICON_CLASSES
|
||||
return data
|
||||
|
||||
|
||||
class _BasedMenuItemAdminForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["parent"].queryset = MenuItem.objects.filter_folders().order_by(
|
||||
"text"
|
||||
)
|
||||
self.fields["parent"].required = False
|
||||
self.fields["parent"].widget = forms.Select(
|
||||
choices=self.fields["parent"].widget.choices
|
||||
) # disable modify buttons
|
||||
|
||||
|
||||
class AppMenuItemAdminForm(_BasedMenuItemAdminForm):
|
||||
"""A form for changing app items."""
|
||||
|
||||
class Meta:
|
||||
model = MenuItem
|
||||
fields = ["order", "parent", "is_hidden"]
|
||||
|
||||
|
||||
class LinkMenuItemAdminForm(_BasedMenuItemAdminForm):
|
||||
"""A form for changing link items."""
|
||||
|
||||
class Meta:
|
||||
model = MenuItem
|
||||
fields = ["text", "url", "classes", "order", "parent", "is_hidden"]
|
||||
widgets = {
|
||||
"url": forms.TextInput(attrs={"size": "100"}),
|
||||
}
|
@ -1,42 +1,58 @@
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
"""Menu item hooks."""
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from allianceauth.menu.constants import DEFAULT_MENU_ITEM_ORDER
|
||||
|
||||
|
||||
class MenuItemHook:
|
||||
"""
|
||||
Auth Hook for generating Side Menu Items
|
||||
"""
|
||||
def __init__(self, text: str, classes: str, url_name: str, order: Optional[int] = None, navactive: List = []):
|
||||
"""
|
||||
:param text: The text shown as menu item, e.g. usually the name of the app.
|
||||
:type text: str
|
||||
:param classes: The classes that should be applied to the menu item icon
|
||||
:type classes: List[str]
|
||||
:param url_name: The name of the Django URL to use
|
||||
:type url_name: str
|
||||
:param order: An integer which specifies the order of the menu item, lowest to highest. Community apps are free to use any order above `1000`. Numbers below are served for Auth.
|
||||
:type order: Optional[int], optional
|
||||
:param navactive: A list of views or namespaces the link should be highlighted on. See [django-navhelper](https://github.com/geelweb/django-navhelper#navactive) for usage. Defaults to the supplied `url_name`.
|
||||
:type navactive: List, optional
|
||||
"""
|
||||
"""Auth Hook for generating side menu items.
|
||||
|
||||
Args:
|
||||
- text: The text shown as menu item, e.g. usually the name of the app.
|
||||
- classes: The classes that should be applied to the menu item icon
|
||||
- url_name: The name of the Django URL to use
|
||||
- order: An integer which specifies the order of the menu item,
|
||||
lowest to highest. Community apps are free to use any order above `1000`.
|
||||
Numbers below are served for Auth.
|
||||
- A list of views or namespaces the link should be highlighted on.
|
||||
See 3rd party package django-navhelper for usage.
|
||||
Defaults to the supplied `url_name`.
|
||||
|
||||
|
||||
Optional:
|
||||
- count is an integer shown next to the menu item as badge when is is not `None`.
|
||||
Apps need to set the count in their child class, e.g. in `render()` method
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
text: str,
|
||||
classes: str,
|
||||
url_name: str,
|
||||
order: Optional[int] = None,
|
||||
navactive: Optional[List[str]] = None,
|
||||
):
|
||||
self.text = text
|
||||
self.classes = classes
|
||||
self.url_name = url_name
|
||||
self.template = 'public/menuitem.html'
|
||||
self.order = order if order is not None else 9999
|
||||
|
||||
# count is an integer shown next to the menu item as badge when count != None
|
||||
# apps need to set the count in their child class, e.g. in render() method
|
||||
self.template = "public/menuitem.html"
|
||||
self.order = order if order is not None else DEFAULT_MENU_ITEM_ORDER
|
||||
self.count = None
|
||||
|
||||
navactive = navactive or []
|
||||
navactive.append(url_name)
|
||||
self.navactive = navactive
|
||||
|
||||
def render(self, request):
|
||||
return render_to_string(self.template,
|
||||
{'item': self},
|
||||
request=request)
|
||||
def __str__(self) -> str:
|
||||
return self.text
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}(text="{self.text}")'
|
||||
|
||||
def render(self, request) -> str:
|
||||
"""Render this menu item and return resulting HTML."""
|
||||
return render_to_string(self.template, {"item": self}, request=request)
|
||||
|
67
allianceauth/menu/managers.py
Normal file
67
allianceauth/menu/managers.py
Normal file
@ -0,0 +1,67 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Case, Q, Value, When
|
||||
|
||||
from allianceauth.hooks import get_hooks
|
||||
|
||||
from .constants import MenuItemType
|
||||
from .core.menu_item_hooks import MenuItemHookParams, gather_params
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .models import MenuItem
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MenuItemQuerySet(models.QuerySet):
|
||||
def filter_folders(self):
|
||||
"""Add filter to include folders only."""
|
||||
return self.filter(hook_hash__isnull=True, url="")
|
||||
|
||||
def annotate_item_type_2(self):
|
||||
"""Add calculated field with item type."""
|
||||
return self.annotate(
|
||||
item_type_2=Case(
|
||||
When(~Q(hook_hash__isnull=True), then=Value(MenuItemType.APP.value)),
|
||||
When(url="", then=Value(MenuItemType.FOLDER.value)),
|
||||
default=Value(MenuItemType.LINK.value),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class MenuItemManagerBase(models.Manager):
|
||||
def sync_all(self):
|
||||
"""Sync all menu items from hooks."""
|
||||
hook_params = self._gather_menu_item_hook_params()
|
||||
self._delete_obsolete_app_items(hook_params)
|
||||
self._update_or_create_app_items(hook_params)
|
||||
|
||||
def _gather_menu_item_hook_params(self) -> list[MenuItemHookParams]:
|
||||
params = [gather_params(hook()) for hook in get_hooks("menu_item_hook")]
|
||||
return params
|
||||
|
||||
def _delete_obsolete_app_items(self, params: list[MenuItemHookParams]):
|
||||
hashes = [obj.hash for obj in params]
|
||||
self.exclude(hook_hash__isnull=True).exclude(hook_hash__in=hashes).delete()
|
||||
|
||||
def _update_or_create_app_items(self, params: list[MenuItemHookParams]):
|
||||
for param in params:
|
||||
try:
|
||||
obj: MenuItem = self.get(hook_hash=param.hash)
|
||||
except self.model.DoesNotExist:
|
||||
self.create(hook_hash=param.hash, order=param.order, text=param.text)
|
||||
else:
|
||||
# if it exists update the text only
|
||||
if obj.text != param.text:
|
||||
obj.text = param.text
|
||||
obj.save()
|
||||
|
||||
logger.debug("Updated menu items from %d menu item hooks", len(params))
|
||||
|
||||
|
||||
MenuItemManager = MenuItemManagerBase.from_queryset(MenuItemQuerySet)
|
@ -1,15 +0,0 @@
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
import logging
|
||||
|
||||
from allianceauth.menu.providers import menu_provider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MenuSyncMiddleware(MiddlewareMixin):
|
||||
|
||||
def __call__(self, request):
|
||||
"""Alliance Auth Menu Sync Middleware"""
|
||||
menu_provider.check_and_sync_menu()
|
||||
return super().__call__(request)
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 4.0.2 on 2022-08-28 14:00
|
||||
# Generated by Django 4.2.9 on 2024-02-15 00:01
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
@ -8,21 +8,88 @@ class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MenuItem',
|
||||
name="MenuItem",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('hook_function', models.CharField(max_length=500)),
|
||||
('icon_classes', models.CharField(max_length=150)),
|
||||
('text', models.CharField(max_length=150)),
|
||||
('url', models.CharField(blank=True, default=None, max_length=2048, null=True)),
|
||||
('rank', models.IntegerField(default=1000)),
|
||||
('hide', models.BooleanField(default=False)),
|
||||
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='menu.menuitem')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"text",
|
||||
models.CharField(
|
||||
db_index=True,
|
||||
help_text="Text to show on menu",
|
||||
max_length=150,
|
||||
verbose_name="text",
|
||||
),
|
||||
),
|
||||
(
|
||||
"order",
|
||||
models.IntegerField(
|
||||
db_index=True,
|
||||
default=9999,
|
||||
help_text="Order of the menu. Lowest First",
|
||||
verbose_name="order",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_hidden",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Hide this menu item.If this item is a folder all items under it will be hidden too",
|
||||
verbose_name="is hidden",
|
||||
),
|
||||
),
|
||||
(
|
||||
"hook_hash",
|
||||
models.CharField(
|
||||
default=None,
|
||||
editable=False,
|
||||
max_length=64,
|
||||
null=True,
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"classes",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
help_text="Font Awesome classes to show as icon on menu, e.g. <code>fa-solid fa-house</code>",
|
||||
max_length=150,
|
||||
verbose_name="icon classes",
|
||||
),
|
||||
),
|
||||
(
|
||||
"url",
|
||||
models.TextField(
|
||||
default="",
|
||||
help_text="External URL this menu items will link to",
|
||||
verbose_name="url",
|
||||
),
|
||||
),
|
||||
(
|
||||
"parent",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="Folder this item is in (optional)",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="children",
|
||||
to="menu.menuitem",
|
||||
verbose_name="folder",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
@ -1,28 +0,0 @@
|
||||
# Generated by Django 4.0.2 on 2022-08-28 14:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('menu', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='hook_function',
|
||||
field=models.CharField(blank=True, default=None, max_length=500, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='icon_classes',
|
||||
field=models.CharField(blank=True, default=None, max_length=150, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='text',
|
||||
field=models.CharField(blank=True, default=None, max_length=150, null=True),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 4.0.8 on 2023-02-05 07:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('menu', '0002_alter_menuitem_hook_function_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='menuitem',
|
||||
index=models.Index(fields=['rank'], name='menu_menuit_rank_e880ab_idx'),
|
||||
),
|
||||
]
|
@ -1,39 +0,0 @@
|
||||
# Generated by Django 4.0.10 on 2023-07-16 11:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('menu', '0003_menuitem_menu_menuit_rank_e880ab_idx'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='hide',
|
||||
field=models.BooleanField(default=False, help_text='Hide this menu item. If this item is a header all items under it will be hidden too.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='icon_classes',
|
||||
field=models.CharField(blank=True, default=None, help_text='Font Awesome classes to show as icon on menu', max_length=150, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, help_text='Parent Header. (Optional)', null=True, on_delete=django.db.models.deletion.SET_NULL, to='menu.menuitem'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='rank',
|
||||
field=models.IntegerField(default=1000, help_text='Order of the menu. Lowest First.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='menuitem',
|
||||
name='text',
|
||||
field=models.CharField(blank=True, default=None, help_text='Text to show on menu', max_length=150, null=True),
|
||||
),
|
||||
]
|
@ -1,174 +1,132 @@
|
||||
import logging
|
||||
from allianceauth.hooks import get_hooks
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
from allianceauth.menu.constants import DEFAULT_FOLDER_ICON_CLASSES
|
||||
|
||||
from .constants import DEFAULT_MENU_ITEM_ORDER, MenuItemType
|
||||
from .core.menu_item_hooks import MenuItemHookCustom
|
||||
from .managers import MenuItemManager
|
||||
|
||||
|
||||
class MenuItem(models.Model):
|
||||
# Auto Generated model from an auth_hook
|
||||
hook_function = models.CharField(
|
||||
max_length=500, default=None, null=True, blank=True)
|
||||
"""An item in the sidebar menu.
|
||||
|
||||
Some of these objects are generated from `MenuItemHook` objects.
|
||||
To avoid confusion we are using the same same field names.user defined
|
||||
"""
|
||||
|
||||
# User Made Model
|
||||
icon_classes = models.CharField(
|
||||
max_length=150, default=None, null=True, blank=True, help_text="Font Awesome classes to show as icon on menu")
|
||||
text = models.CharField(
|
||||
max_length=150, default=None, null=True, blank=True, help_text="Text to show on menu")
|
||||
url = models.CharField(max_length=2048, default=None,
|
||||
null=True, blank=True)
|
||||
|
||||
# Put it under a header?
|
||||
max_length=150,
|
||||
db_index=True,
|
||||
verbose_name=_("text"),
|
||||
help_text=_("Text to show on menu"),
|
||||
)
|
||||
order = models.IntegerField(
|
||||
default=DEFAULT_MENU_ITEM_ORDER,
|
||||
db_index=True,
|
||||
verbose_name=_("order"),
|
||||
help_text=_("Order of the menu. Lowest First"),
|
||||
)
|
||||
parent = models.ForeignKey(
|
||||
'self', on_delete=models.SET_NULL, null=True, blank=True, help_text="Parent Header. (Optional)")
|
||||
"self",
|
||||
on_delete=models.SET_NULL,
|
||||
default=None,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="children",
|
||||
verbose_name=_("folder"),
|
||||
help_text=_("Folder this item is in (optional)"),
|
||||
)
|
||||
is_hidden = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("is hidden"),
|
||||
help_text=_(
|
||||
"Hide this menu item."
|
||||
"If this item is a folder all items under it will be hidden too"
|
||||
),
|
||||
)
|
||||
|
||||
# Put it where? lowest first
|
||||
rank = models.IntegerField(default=1000, help_text="Order of the menu. Lowest First.")
|
||||
# app related properties
|
||||
hook_hash = models.CharField(
|
||||
max_length=64, default=None, null=True, unique=True, editable=False
|
||||
) # hash of a menu item hook. Must be nullable for unique comparison.
|
||||
|
||||
# Hide it fully? Hiding a parent will hide all it's children
|
||||
hide = models.BooleanField(default=False, help_text="Hide this menu item. If this item is a header all items under it will be hidden too.")
|
||||
# user defined properties
|
||||
classes = models.CharField(
|
||||
max_length=150,
|
||||
default="",
|
||||
blank=True,
|
||||
verbose_name=_("icon classes"),
|
||||
help_text=_(
|
||||
"Font Awesome classes to show as icon on menu, "
|
||||
"e.g. <code>fa-solid fa-house</code>"
|
||||
),
|
||||
)
|
||||
url = models.TextField(
|
||||
default="",
|
||||
verbose_name=_("url"),
|
||||
help_text=_("External URL this menu items will link to"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=['rank', ]),
|
||||
]
|
||||
objects = MenuItemManager()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.text
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.hook_hash:
|
||||
self.hook_hash = None # empty strings can create problems
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def classes(self): # Helper function to make this model closer to the hook functions
|
||||
return self.icon_classes
|
||||
def item_type(self) -> MenuItemType:
|
||||
"""Return the type of this menu item."""
|
||||
if self.hook_hash:
|
||||
return MenuItemType.APP
|
||||
|
||||
@staticmethod
|
||||
def hook_to_name(mh):
|
||||
return f"{mh.__class__.__module__}.{mh.__class__.__name__}"
|
||||
if not self.url:
|
||||
return MenuItemType.FOLDER
|
||||
|
||||
@staticmethod
|
||||
def sync_hook_models():
|
||||
# TODO define aa way for hooks to predefine a "parent" to create a sub menu from modules
|
||||
menu_hooks = get_hooks('menu_item_hook')
|
||||
hook_functions = []
|
||||
for hook in menu_hooks:
|
||||
mh = hook()
|
||||
cls = MenuItem.hook_to_name(mh)
|
||||
try:
|
||||
# if it exists update the text only
|
||||
# Users can adjust ranks so lets not change it if they have.
|
||||
mi = MenuItem.objects.get(hook_function=cls)
|
||||
mi.text = getattr(mh, "text", mh.__class__.__name__)
|
||||
mi.save()
|
||||
except MenuItem.DoesNotExist:
|
||||
# This is a new hook, Make the database model.
|
||||
MenuItem.objects.create(
|
||||
hook_function=cls,
|
||||
rank=getattr(mh, "order", 500),
|
||||
text=getattr(mh, "text", mh.__class__.__name__)
|
||||
)
|
||||
hook_functions.append(cls)
|
||||
return MenuItemType.LINK
|
||||
|
||||
# Get rid of any legacy hooks from modules removed
|
||||
MenuItem.objects.filter(hook_function__isnull=False).exclude(
|
||||
hook_function__in=hook_functions).delete()
|
||||
@property
|
||||
def is_app_item(self) -> bool:
|
||||
"""Return True if this is an app item, else False."""
|
||||
return self.item_type is MenuItemType.APP
|
||||
|
||||
@classmethod
|
||||
def filter_items(cls, menu_item: dict):
|
||||
"""
|
||||
filter any items with no valid children from a menu
|
||||
"""
|
||||
count_items = len(menu_item['items'])
|
||||
if count_items: # if we have children confirm we can see them
|
||||
for i in menu_item['items']:
|
||||
if len(i['render']) == 0:
|
||||
count_items -= 1
|
||||
if count_items == 0: # no children left dont render header
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
@property
|
||||
def is_child(self) -> bool:
|
||||
"""Return True if this item is a child, else False."""
|
||||
return bool(self.parent_id)
|
||||
|
||||
@classmethod
|
||||
def render_menu(cls, request):
|
||||
"""
|
||||
Return the sorted side menu items with any items the user can't see removed.
|
||||
"""
|
||||
# Override all the items to the bs5 theme
|
||||
template = "menu/menu-item-bs5.html"
|
||||
# TODO discuss permissions for user defined links
|
||||
@property
|
||||
def is_folder(self) -> bool:
|
||||
"""Return True if this item is a folder, else False."""
|
||||
return self.item_type is MenuItemType.FOLDER
|
||||
|
||||
# Turn all the hooks into functions
|
||||
menu_hooks = get_hooks('menu_item_hook')
|
||||
items = {}
|
||||
for fn in menu_hooks:
|
||||
f = fn()
|
||||
items[cls.hook_to_name(f)] = f
|
||||
@property
|
||||
def is_link_item(self) -> bool:
|
||||
"""Return True if this item is a link item, else False."""
|
||||
return self.item_type is MenuItemType.LINK
|
||||
|
||||
menu_items = MenuItem.objects.all().order_by("rank")
|
||||
@property
|
||||
def is_user_defined(self) -> bool:
|
||||
"""Return True if this item is user defined."""
|
||||
return self.item_type is not MenuItemType.APP
|
||||
|
||||
menu = {}
|
||||
for mi in menu_items:
|
||||
if mi.hide:
|
||||
# hidden item, skip it completely
|
||||
continue
|
||||
try:
|
||||
_cnt = 0
|
||||
_render = None
|
||||
if mi.hook_function:
|
||||
# This is a module hook, so we need to render it as the developer intended
|
||||
# TODO add a new attribute for apps that want to override it in the new theme
|
||||
items[mi.hook_function].template = template
|
||||
_render = items[mi.hook_function].render(request)
|
||||
_cnt = items[mi.hook_function].count
|
||||
else:
|
||||
# This is a user defined menu item so we render it with defaults.
|
||||
_render = render_to_string(template,
|
||||
{'item': mi},
|
||||
request=request)
|
||||
def to_hook_obj(self) -> MenuItemHookCustom:
|
||||
"""Convert to hook object for rendering."""
|
||||
if self.is_app_item:
|
||||
raise ValueError("The related hook objects should be used for app items.")
|
||||
|
||||
parent = mi.id
|
||||
if mi.parent_id: # Set it if present
|
||||
parent = mi.parent_id
|
||||
hook_obj = MenuItemHookCustom(
|
||||
text=self.text, classes=self.classes, url_name="", order=self.order
|
||||
)
|
||||
hook_obj.navactive = []
|
||||
if self.is_folder and not self.classes:
|
||||
hook_obj.classes = DEFAULT_FOLDER_ICON_CLASSES
|
||||
|
||||
if parent not in menu: # this will cause the menu headers to be out of order
|
||||
menu[parent] = {"items": [],
|
||||
"count": 0,
|
||||
"render": None,
|
||||
"text": "None",
|
||||
"rank": 9999,
|
||||
}
|
||||
_mi = {
|
||||
"count": _cnt,
|
||||
"render": _render,
|
||||
"text": mi.text,
|
||||
"rank": mi.rank,
|
||||
"classes": (mi.icon_classes if mi.icon_classes != "" else "fa-solid fa-folder"),
|
||||
"hide": mi.hide
|
||||
}
|
||||
|
||||
if parent != mi.id:
|
||||
# this is a sub item
|
||||
menu[parent]["items"].append(_mi)
|
||||
if _cnt:
|
||||
#add its count to the header count
|
||||
menu[parent]["count"] += _cnt
|
||||
else:
|
||||
if len(menu[parent]["items"]):
|
||||
# this is a top folder dont update the count.
|
||||
del(_mi["count"])
|
||||
menu[parent].update(_mi)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
|
||||
# reset to list
|
||||
menu = list(menu.values())
|
||||
|
||||
# sort the menu list as the parents may be out of order.
|
||||
menu.sort(key=lambda i: i['rank'])
|
||||
|
||||
# ensure no empty groups
|
||||
menu = filter(cls.filter_items, menu)
|
||||
|
||||
return menu
|
||||
hook_obj.url = self.url
|
||||
hook_obj.is_folder = self.is_folder
|
||||
hook_obj.html_id = f"id-folder-{self.id}" if self.is_folder else ""
|
||||
return hook_obj
|
||||
|
@ -1,49 +0,0 @@
|
||||
import logging
|
||||
|
||||
from django.core.cache import cache
|
||||
|
||||
from allianceauth.menu.models import MenuItem
|
||||
from allianceauth.utils.django import StartupCommand
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MENU_SYNC_CACHE_KEY = "ALLIANCEAUTH-MENU-SYNCED"
|
||||
MENU_CACHE_KEY = "ALLIANCEAUTH-MENU-CACHE"
|
||||
|
||||
|
||||
class MenuProvider():
|
||||
|
||||
def clear_synced_flag(self) -> bool:
|
||||
return cache.delete(MENU_SYNC_CACHE_KEY)
|
||||
|
||||
def set_synced_flag(self) -> bool:
|
||||
return cache.set(MENU_SYNC_CACHE_KEY, True)
|
||||
|
||||
def get_synced_flag(self) -> bool:
|
||||
return cache.get(MENU_SYNC_CACHE_KEY, False)
|
||||
|
||||
def sync_menu_models(self):
|
||||
MenuItem.sync_hook_models()
|
||||
self.set_synced_flag()
|
||||
|
||||
def check_and_sync_menu(self) -> None:
|
||||
if self.get_synced_flag():
|
||||
# performance hit to each page view to ensure tests work.
|
||||
# tests clear DB but not cache.
|
||||
# TODO rethink all of this?
|
||||
if MenuItem.objects.all().count() > 0:
|
||||
logger.debug("Menu Hooks Synced")
|
||||
else:
|
||||
self.sync_menu_models()
|
||||
else:
|
||||
logger.debug("Syncing Menu Hooks")
|
||||
self.sync_menu_models()
|
||||
|
||||
def get_and_cache_menu(self):
|
||||
pass
|
||||
|
||||
def clear_menu_cache(self):
|
||||
pass
|
||||
|
||||
|
||||
menu_provider = MenuProvider()
|
@ -0,0 +1,13 @@
|
||||
{% extends "admin/change_list.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block object-tools-items %}
|
||||
|
||||
{{ block.super }}
|
||||
<li>
|
||||
<a href="{% url 'admin:menu_menuitem_add' %}?type={{ folder_type }}" class="addlink">
|
||||
{% translate "Add folder" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock %}
|
@ -1,7 +1,3 @@
|
||||
{% for data in menu_items %}
|
||||
{% if data.items|length > 0 %}
|
||||
{% include "menu/menu-item-bs5.html" with item=data %}
|
||||
{% else %}
|
||||
{{ data.render }}
|
||||
{% endif %}
|
||||
{% for item in menu_items %}
|
||||
{{ item.html }}
|
||||
{% endfor %}
|
||||
|
@ -1,37 +1,57 @@
|
||||
{% load i18n %}
|
||||
{% load navactive %}
|
||||
|
||||
{% if not item.hide %}
|
||||
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">
|
||||
<i class="nav-link {{ item.classes }} fa-fw align-self-center me-3 {% if item.navactive %}{% navactive request item.navactive|join:' ' %}{% endif %}" {% if item.items|length %} type="button" data-bs-toggle="collapse" data-bs-target="#id-{{ item.text|slugify }}" aria-expanded="false" aria-controls="" {% endif %}></i>
|
||||
<a class="nav-link flex-fill align-self-center" {% if item.items|length %} type="button" data-bs-toggle="collapse" data-bs-target="#id-{{ item.text|slugify }}" aria-expanded="false" aria-controls="" {% endif %}
|
||||
href="{% if item.url_name %}{% url item.url_name %}{% else %}{{ item.url }}{% endif %}">
|
||||
{% translate item.text %}
|
||||
</a>
|
||||
|
||||
{% if item.count >= 1 %}
|
||||
<span class="badge bg-primary m-2 align-self-center {% if item.items|length == 0 %}me-4{% endif %}">
|
||||
{{ item.count }}
|
||||
</span>
|
||||
{% elif item.url %}
|
||||
<span class="pill m-2 me-4 align-self-center fas fa-external-link-alt"></span>
|
||||
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">
|
||||
<i
|
||||
class="nav-link {{ item.classes }} fa-fw align-self-center me-3 {% if item.navactive %}{% navactive request item.navactive|join:' ' %}{% endif %}"
|
||||
{% if item.is_folder %}
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#{{ item.html_id }}"
|
||||
aria-expanded="false"
|
||||
aria-controls=""
|
||||
{% endif %}>
|
||||
</i>
|
||||
<a
|
||||
class="nav-link flex-fill align-self-center me-auto"
|
||||
{% if item.is_folder %}
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#{{ item.html_id }}"
|
||||
aria-expanded="false"
|
||||
aria-controls=""
|
||||
{% endif %}
|
||||
href="{% if item.url_name %}{% url item.url_name %}{% else %}{{ item.url }}{% endif %}">
|
||||
{% translate item.text %}
|
||||
</a>
|
||||
|
||||
{% if item.items|length > 0 %}
|
||||
<span
|
||||
class="pill m-2 me-4 align-self-center fas fa-solid fa-chevron-down"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#id-{{ item.text|slugify }}"
|
||||
aria-expanded="false"
|
||||
aria-controls="">
|
||||
</span>
|
||||
<!--<hr class="m-0 w-100">-->
|
||||
<ul class="collapse ps-1 w-100 border-start rounded-start border-light border-3" id="id-{{ item.text|slugify }}">
|
||||
{% for sub_item in item.items %}
|
||||
{{ sub_item.render }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if item.count >= 1 %}
|
||||
<span class="badge bg-primary m-2 align-self-center{% if not item.is_folder %} me-2{% endif %}">
|
||||
{{ item.count }}
|
||||
</span>
|
||||
{% elif item.url %}
|
||||
<span class="pill m-2 me-4 align-self-center fas fa-external-link-alt"></span>
|
||||
{% endif %}
|
||||
|
||||
{% if item.is_folder %}
|
||||
<span
|
||||
class="pill m-2 align-self-center collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#{{ item.html_id }}"
|
||||
aria-expanded="false"
|
||||
aria-controls=""
|
||||
>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</span>
|
||||
<ul
|
||||
class="collapse ps-1 w-100 border-start rounded-start border-light border-3"
|
||||
id="{{ item.html_id }}">
|
||||
{% for sub_item in item.children %}
|
||||
{{ sub_item }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
3
allianceauth/menu/templates/menu/menu-logo.html
Normal file
3
allianceauth/menu/templates/menu/menu-logo.html
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="align-items-center text-center">
|
||||
{% include "bundles/image-auth-logo.html" %}
|
||||
</div>
|
@ -2,57 +2,89 @@
|
||||
{% load evelinks %}
|
||||
{% load theme_tags %}
|
||||
|
||||
<div style="z-index:5;" class="w100 d-flex flex-column justify-content-center align-items-center text-center pb-2">
|
||||
{% if user.is_authenticated %}
|
||||
{% if request.user.profile.main_character %}
|
||||
{% with request.user.profile.main_character as main %}
|
||||
<div class="p-2 position-relative m-2">
|
||||
<div id="aa-user-info" class="w-100 d-flex flex-column justify-content-center align-items-center text-center py-1 border-top border-secondary {% if not user.is_authenticated %}position-absolute bottom-0{% endif %}">
|
||||
<div class="d-flex mb-0 w-100">
|
||||
<div class="p-2 position-relative m-2">
|
||||
{% if user.is_authenticated %}
|
||||
{% with request.user.profile.main_character as main %}
|
||||
<img class="rounded-circle" src="{{ main.character_id|character_portrait_url:64 }}" alt="{{ main.character_name }}">
|
||||
<img class="rounded-circle position-absolute bottom-0 start-0" src="{{ main.corporation_logo_url_32 }}" alt="{{ main.corporation_name }}">
|
||||
|
||||
{% if main.alliance_id %}
|
||||
<img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.alliance_logo_url_32 }}" alt="{{ main.alliance_name }}">
|
||||
{% elif main.faction_id %}
|
||||
<img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.faction_logo_url_32 }}" alt="{{ main.faction_name }}">
|
||||
{% endif %}
|
||||
</div>
|
||||
<h5>{{ main.character_name }}</h5>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<img class="rounded-circle m-2" src="{{ 1|character_portrait_url:32 }}" alt="{% translate 'No Main Character!' %}">
|
||||
<h5>{% translate "No Main Character!" %}</h5>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
{% include "bundles/image-auth-logo.html" with logo_width="64px" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="align-self-center text-start">
|
||||
{% if user.is_authenticated %}
|
||||
{% with request.user.profile.main_character as main %}
|
||||
<h5 class="m-0">{{ main.character_name }}</h5>
|
||||
<p class="m-0 small">{{ main.corporation_name }}</p>
|
||||
{% if main.alliance_id %}
|
||||
<p class="m-0 small">{{ main.alliance_name }}</p>
|
||||
{% elif main.faction_id %}
|
||||
<p class="m-0 small">{{ main.faction_name }}</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<h5 class="m-0">{{ SITE_NAME }}</h5>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="ms-auto dropup">
|
||||
<button type="button" class="h-100 btn" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa-solid fa-gear fa-fw text-light"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu" style="min-width: 200px;">
|
||||
<li><h6 class="dropdown-header">{% translate "Language" %}</h6></li>
|
||||
<li>
|
||||
<a class="dropdown-item">{% include "public/lang_select.html" %}</a>
|
||||
</li>
|
||||
<li><h6 class="dropdown-header">{% translate "Theme" %}</h6></li>
|
||||
|
||||
{% theme_select %}
|
||||
{% endif %}
|
||||
<li>
|
||||
<a class="dropdown-item">
|
||||
{% theme_select %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<div class="btn-group m-2">
|
||||
<button type="button" class="btn btn-secondary p-1">
|
||||
{% include "public/lang_select.html" %}
|
||||
</button>
|
||||
|
||||
{% if user.is_superuser %}
|
||||
<a role="button" class="btn btn btn-secondary d-flex" href="{% url 'admin:index' %}">
|
||||
<span class="align-self-center">{% translate "Admin" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="btn-group m-2">
|
||||
{% if user.is_authenticated %}
|
||||
<a role="button" class="btn btn-info" href="{% url 'authentication:token_management' %}" title="Token Management"><i class="fa-solid fa-user-lock fa-fw"></i></a>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_superuser %}
|
||||
<a role="button" class="btn btn-info" href="https://allianceauth.readthedocs.io/" title="Alliance Auth Documentation"><i class="fa-solid fa-book fa-fw"></i></a>
|
||||
<a role="button" class="btn btn-info" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i></a>
|
||||
<a role="button" class="btn btn-info" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i></a>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<a role="button" class="btn btn-danger" href="{% url 'logout' %}" title="{% translate 'Sign Out' %}"><i class="fa-solid fa-right-from-bracket fa-fw"></i></a>
|
||||
{% else %}
|
||||
<a role="button" class="btn btn-success" href="{% url 'authentication:login' %}" title="{% translate 'Sign In' %}"> <i class="fa-solid fa-right-to-bracket fa-fw"></i></a>
|
||||
{% endif %}
|
||||
{% if user.is_superuser %}
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header">{% translate "Super User" %}</h6></li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="https://allianceauth.readthedocs.io/" title="Alliance Auth Documentation"><i class="fa-solid fa-book fa-fw"></i> Alliance Auth Documentation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i> Alliance Auth Discord</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i> Alliance Auth Git</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'admin:index' %}">
|
||||
<i class="fa-solid fa-gear fa-fw"></i> {% translate "Admin" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{% if user.is_authenticated %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'authentication:token_management' %}">
|
||||
<i class="fa-solid fa-user-lock fa-fw"></i> Token Management
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item text-danger" href="{% url 'logout' %}" title="{% translate 'Sign Out' %}"><i class="fa-solid fa-right-from-bracket fa-fw "></i> {% translate 'Sign Out' %}</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
<a class="dropdown-item text-success" href="{% url 'authentication:login' %}" title="{% translate 'Sign In' %}"> <i class="fa-solid fa-right-to-bracket fa-fw "></i> {% translate 'Sign In' %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
<div class="col-auto px-0">
|
||||
<div class="collapse collapse-horizontal" tabindex="-1" id="sidebar">
|
||||
<div style="width: 350px;">
|
||||
<div class="nav-padding navbar-dark bg-dark text-light px-0 d-flex flex-column overflow-hidden vh-100 auth-logo">
|
||||
<div>
|
||||
<div class="nav-padding navbar-dark bg-dark text-light px-0 d-flex flex-column overflow-hidden vh-100 {% if not user.is_authenticated %}position-relative{% endif %}">
|
||||
{% if user.is_authenticated %}
|
||||
<ul style="z-index:5;" id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2">
|
||||
<ul id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2">
|
||||
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">
|
||||
<i class="nav-link fas fa-tachometer-alt fa-fw align-self-center me-3 {% navactive request 'authentication:dashboard' %}"></i>
|
||||
<a class="nav-link flex-fill align-self-center" href="{% url 'authentication:dashboard' %}">
|
||||
@ -15,8 +15,10 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% sorted_menu_items %}
|
||||
{% menu_items %}
|
||||
</ul>
|
||||
|
||||
{% include 'menu/menu-logo.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'menu/menu-user.html' %}
|
||||
|
32
allianceauth/menu/templatetags/menu_items.py
Normal file
32
allianceauth/menu/templatetags/menu_items.py
Normal file
@ -0,0 +1,32 @@
|
||||
"""Template tags for rendering the classic side menu."""
|
||||
|
||||
from django import template
|
||||
from django.http import HttpRequest
|
||||
|
||||
from allianceauth.hooks import get_hooks
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
# TODO: Show user created menu items
|
||||
# TODO: Apply is_hidden feature for BS3 type items
|
||||
|
||||
|
||||
@register.inclusion_tag("public/menublock.html", takes_context=True)
|
||||
def menu_items(context: dict) -> dict:
|
||||
"""Render menu items for classic dashboard."""
|
||||
items = render_menu(context["request"])
|
||||
return {"menu_items": items}
|
||||
|
||||
|
||||
def render_menu(request: HttpRequest):
|
||||
"""Return the rendered side menu for including in a template.
|
||||
|
||||
This function is creating a BS3 style menu.
|
||||
"""
|
||||
|
||||
hooks = get_hooks("menu_item_hook")
|
||||
raw_items = [fn() for fn in hooks]
|
||||
raw_items.sort(key=lambda i: i.order)
|
||||
menu_items = [item.render(request) for item in raw_items]
|
||||
return menu_items
|
@ -1,34 +1,174 @@
|
||||
"""Template tags for rendering the new side menu.
|
||||
|
||||
Documentation of the render logic
|
||||
---------------------------------
|
||||
|
||||
The are 3 types of menu items:
|
||||
|
||||
- App entries: Generated by hooks from Django apps
|
||||
- Link entries: Linking to external pages. User created.
|
||||
- Folder: Grouping together several app or link entries. User created.
|
||||
|
||||
The MenuItem model holds the current list of all menu items.
|
||||
|
||||
App entries are linked to a `MenuItemHook` object in the respective Django app.
|
||||
Those hook objects contain dynamic logic in a `render()` method,
|
||||
which must be executed when rendering for the current request.
|
||||
|
||||
Since the same template must be used to render all items, link entries and folders
|
||||
are converted to `MenuItemHookCustom` objects, a sub class of `MenuItemHook`.
|
||||
This ensures the template only rendered objects of one specific type or sub-type.
|
||||
|
||||
The rendered menu items are finally collected in a list of RenderedMenuItem objects,
|
||||
which is used to render the complete menu.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from django import template
|
||||
from django.db.models import QuerySet
|
||||
from django.http import HttpRequest
|
||||
|
||||
from allianceauth.hooks import get_hooks
|
||||
from allianceauth.menu.core import menu_item_hooks, smart_sync
|
||||
from allianceauth.menu.models import MenuItem
|
||||
|
||||
from allianceauth.services.auth_hooks import MenuItemHook
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
def process_menu_items(hooks, request):
|
||||
_menu_items = list()
|
||||
items = [fn() for fn in hooks]
|
||||
items.sort(key=lambda i: i.order)
|
||||
for item in items:
|
||||
_menu_items.append(item.render(request))
|
||||
return _menu_items
|
||||
@register.inclusion_tag("menu/menu-block.html", takes_context=True)
|
||||
def menu_items(context: dict) -> dict:
|
||||
"""Render menu items for new dashboards."""
|
||||
smart_sync.sync_menu()
|
||||
|
||||
items = render_menu(context["request"])
|
||||
return {"menu_items": items}
|
||||
|
||||
|
||||
@register.inclusion_tag('public/menublock.html', takes_context=True)
|
||||
def menu_items(context):
|
||||
request = context['request']
|
||||
@dataclass
|
||||
class RenderedMenuItem:
|
||||
"""A rendered menu item.
|
||||
|
||||
return {
|
||||
'menu_items': process_menu_items(get_hooks('menu_item_hook'), request),
|
||||
}
|
||||
These objects can be rendered with the menu-block template.
|
||||
"""
|
||||
|
||||
menu_item: MenuItem
|
||||
|
||||
children: List["RenderedMenuItem"] = field(default_factory=list)
|
||||
count: Optional[int] = None
|
||||
html: str = ""
|
||||
html_id: str = ""
|
||||
|
||||
@property
|
||||
def is_folder(self) -> bool:
|
||||
"""Return True if this item is a folder."""
|
||||
return self.menu_item.is_folder
|
||||
|
||||
def update_html(self, request: HttpRequest, template: str):
|
||||
"""Render this menu item with defaults and set HTML ID."""
|
||||
hook_obj = self.menu_item.to_hook_obj()
|
||||
hook_obj.template = template
|
||||
hook_obj.count = self.count
|
||||
if self.is_folder:
|
||||
hook_obj.children = [child.html for child in self.children]
|
||||
|
||||
self.html = hook_obj.render(request)
|
||||
self.html_id = hook_obj.html_id
|
||||
|
||||
|
||||
@register.inclusion_tag('menu/menu-block.html', takes_context=True)
|
||||
def sorted_menu_items(context):
|
||||
request = context['request']
|
||||
menu_items = MenuItem.render_menu(request)
|
||||
return {
|
||||
'menu_items':menu_items
|
||||
}
|
||||
def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
|
||||
"""Return the rendered side menu for including in a template.
|
||||
|
||||
This function is creating BS5 style menus.
|
||||
"""
|
||||
hook_items = _gather_menu_items_from_hooks()
|
||||
|
||||
# Menu items needs to be rendered with the new BS5 template
|
||||
bs5_template = "menu/menu-item-bs5.html"
|
||||
|
||||
rendered_items: Dict[int, RenderedMenuItem] = {}
|
||||
menu_items: QuerySet[MenuItem] = MenuItem.objects.order_by(
|
||||
"parent", "order", "text"
|
||||
)
|
||||
for item in menu_items:
|
||||
if item.is_hidden:
|
||||
continue # do not render hidden items
|
||||
|
||||
if item.is_app_item:
|
||||
rendered_item = _render_app_item(request, hook_items, item, bs5_template)
|
||||
elif item.is_link_item:
|
||||
rendered_item = _render_link_item(request, item, bs5_template)
|
||||
elif item.is_folder:
|
||||
rendered_item = RenderedMenuItem(item) # we render these items later
|
||||
else:
|
||||
raise NotImplementedError("Unknown menu item type")
|
||||
|
||||
if item.is_child:
|
||||
try:
|
||||
parent = rendered_items[item.parent_id]
|
||||
except KeyError:
|
||||
continue # do not render children of hidden folders
|
||||
|
||||
parent.children.append(rendered_item)
|
||||
if rendered_item.count is not None:
|
||||
if parent.count is None:
|
||||
parent.count = 0
|
||||
parent.count += rendered_item.count
|
||||
|
||||
else:
|
||||
rendered_items[item.id] = rendered_item
|
||||
|
||||
_remove_empty_folders(rendered_items)
|
||||
|
||||
_render_folder_items(request, rendered_items, bs5_template)
|
||||
|
||||
return list(rendered_items.values())
|
||||
|
||||
|
||||
def _gather_menu_items_from_hooks() -> Dict[str, MenuItemHook]:
|
||||
hook_items = {}
|
||||
for hook in get_hooks("menu_item_hook"):
|
||||
f = hook()
|
||||
hook_items[menu_item_hooks.generate_hash(f)] = f
|
||||
return hook_items
|
||||
|
||||
|
||||
def _render_app_item(
|
||||
request: HttpRequest, hook_items: dict, item: MenuItem, new_template: str
|
||||
) -> RenderedMenuItem:
|
||||
# This is a module hook, so we need to render it as the developer intended
|
||||
# TODO add a new attribute for apps that want to override it in the new theme
|
||||
hook_item = hook_items[item.hook_hash]
|
||||
hook_item.template = new_template
|
||||
html = hook_item.render(request)
|
||||
count = hook_item.count
|
||||
rendered_item = RenderedMenuItem(menu_item=item, count=count, html=html)
|
||||
return rendered_item
|
||||
|
||||
|
||||
def _render_link_item(
|
||||
request: HttpRequest, item: MenuItem, new_template: str
|
||||
) -> RenderedMenuItem:
|
||||
rendered_item = RenderedMenuItem(menu_item=item)
|
||||
rendered_item.update_html(request, template=new_template)
|
||||
return rendered_item
|
||||
|
||||
|
||||
def _render_folder_items(
|
||||
request: HttpRequest, rendered_items: Dict[int, RenderedMenuItem], new_template: str
|
||||
):
|
||||
for item in rendered_items.values():
|
||||
if item.menu_item.is_folder:
|
||||
item.update_html(request=request, template=new_template)
|
||||
|
||||
|
||||
def _remove_empty_folders(rendered_items: Dict[int, RenderedMenuItem]):
|
||||
ids_to_remove = []
|
||||
for item_id, item in rendered_items.items():
|
||||
if item.is_folder and not item.children:
|
||||
ids_to_remove.append(item_id)
|
||||
|
||||
for item_id in ids_to_remove:
|
||||
del rendered_items[item_id]
|
||||
|
0
allianceauth/menu/tests/__init__.py
Normal file
0
allianceauth/menu/tests/__init__.py
Normal file
0
allianceauth/menu/tests/core/__init__.py
Normal file
0
allianceauth/menu/tests/core/__init__.py
Normal file
63
allianceauth/menu/tests/core/test_menu_item_hooks.py
Normal file
63
allianceauth/menu/tests/core/test_menu_item_hooks.py
Normal file
@ -0,0 +1,63 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.menu.core.menu_item_hooks import (
|
||||
MenuItemHookCustom,
|
||||
gather_params,
|
||||
generate_hash,
|
||||
)
|
||||
from allianceauth.menu.tests.factories import create_menu_item_hook_function
|
||||
|
||||
|
||||
class TestGenerateHash(TestCase):
|
||||
def test_should_generate_same_hash(self):
|
||||
# given
|
||||
hook = create_menu_item_hook_function()
|
||||
|
||||
# when
|
||||
result_1 = generate_hash(hook())
|
||||
result_2 = generate_hash(hook())
|
||||
|
||||
# then
|
||||
self.assertIsInstance(result_1, str)
|
||||
self.assertEqual(result_1, result_2)
|
||||
|
||||
def test_should_generate_different_hashes(self):
|
||||
# given
|
||||
hook_1 = create_menu_item_hook_function()
|
||||
hook_2 = create_menu_item_hook_function()
|
||||
|
||||
# when
|
||||
result_1 = generate_hash(hook_1())
|
||||
result_2 = generate_hash(hook_2())
|
||||
|
||||
# then
|
||||
self.assertNotEqual(result_1, result_2)
|
||||
|
||||
|
||||
class TestExtractParams(TestCase):
|
||||
def test_should_return_params(self):
|
||||
# given
|
||||
hook = create_menu_item_hook_function(text="Alpha", order=42)
|
||||
|
||||
# when
|
||||
result = gather_params(hook())
|
||||
|
||||
# then
|
||||
self.assertEqual(result.text, "Alpha")
|
||||
self.assertEqual(result.order, 42)
|
||||
self.assertIsInstance(result.hash, str)
|
||||
|
||||
|
||||
class TestMenuItemHookCustom(TestCase):
|
||||
def test_should_create_minimal(self):
|
||||
# when
|
||||
obj = MenuItemHookCustom(text="text", classes="classes", url_name="url_name")
|
||||
|
||||
# then
|
||||
self.assertEqual(obj.text, "text")
|
||||
self.assertEqual(obj.classes, "classes")
|
||||
self.assertEqual(obj.url_name, "url_name")
|
||||
self.assertEqual(obj.url, "")
|
||||
self.assertIsNone(obj.is_folder)
|
||||
self.assertEqual(obj.html_id, "")
|
||||
self.assertListEqual(obj.children, [])
|
42
allianceauth/menu/tests/core/test_smart_sync.py
Normal file
42
allianceauth/menu/tests/core/test_smart_sync.py
Normal file
@ -0,0 +1,42 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.menu.core import smart_sync
|
||||
from allianceauth.menu.tests.factories import create_link_menu_item
|
||||
from allianceauth.menu.tests.utils import PACKAGE_PATH
|
||||
|
||||
|
||||
@patch(PACKAGE_PATH + ".models.MenuItem.objects.sync_all", spec=True)
|
||||
class TestSmartSync(TestCase):
|
||||
def test_should_sync_after_reset(self, mock_sync_all):
|
||||
# given
|
||||
smart_sync.reset_menu_items_sync()
|
||||
mock_sync_all.reset_mock()
|
||||
|
||||
# when
|
||||
smart_sync.sync_menu()
|
||||
|
||||
# then
|
||||
self.assertTrue(mock_sync_all.called)
|
||||
|
||||
def test_should_sync_when_sync_flag_is_set_but_no_items_in_db(self, mock_sync_all):
|
||||
# given
|
||||
smart_sync._record_menu_was_synced()
|
||||
|
||||
# when
|
||||
smart_sync.sync_menu()
|
||||
|
||||
# then
|
||||
self.assertTrue(mock_sync_all.called)
|
||||
|
||||
def test_should_not_sync_when_sync_flag_is_set_and_items_in_db(self, mock_sync_all):
|
||||
# given
|
||||
smart_sync._record_menu_was_synced()
|
||||
create_link_menu_item()
|
||||
|
||||
# when
|
||||
smart_sync.sync_menu()
|
||||
|
||||
# then
|
||||
self.assertFalse(mock_sync_all.called)
|
95
allianceauth/menu/tests/factories.py
Normal file
95
allianceauth/menu/tests/factories.py
Normal file
@ -0,0 +1,95 @@
|
||||
from itertools import count
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from allianceauth.menu.core import menu_item_hooks
|
||||
from allianceauth.menu.models import MenuItem
|
||||
from allianceauth.menu.templatetags.menu_menu_items import RenderedMenuItem
|
||||
from allianceauth.services.auth_hooks import MenuItemHook
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
|
||||
def create_user(permissions=None, **kwargs) -> User:
|
||||
num = next(counter_user)
|
||||
params = {"username": f"test_user_{num}"}
|
||||
params.update(kwargs)
|
||||
user = User.objects.create(**params)
|
||||
if permissions:
|
||||
user = AuthUtils.add_permissions_to_user_by_name(perms=permissions, user=user)
|
||||
return user
|
||||
|
||||
|
||||
def create_menu_item_hook(**kwargs) -> MenuItemHook:
|
||||
num = next(counter_menu_item_hook)
|
||||
new_class = type(f"GeneratedMenuItem{num}", (MenuItemHook,), {})
|
||||
|
||||
count = kwargs.pop("count", None)
|
||||
params = {
|
||||
"text": f"Dummy App #{num}",
|
||||
"classes": "fa-solid fa-users-gear",
|
||||
"url_name": "groupmanagement:management",
|
||||
}
|
||||
params.update(kwargs)
|
||||
obj = new_class(**params)
|
||||
for key, value in params.items():
|
||||
setattr(obj, key, value)
|
||||
|
||||
obj.count = count
|
||||
return obj
|
||||
|
||||
|
||||
def create_menu_item_hook_function(**kwargs):
|
||||
obj = create_menu_item_hook(**kwargs)
|
||||
return lambda: obj
|
||||
|
||||
|
||||
def create_link_menu_item(**kwargs) -> MenuItem:
|
||||
num = next(counter_menu_item)
|
||||
params = {
|
||||
"url": f"https://www.example.com/{num}",
|
||||
}
|
||||
params.update(kwargs)
|
||||
return _create_menu_item(**params)
|
||||
|
||||
|
||||
def create_app_menu_item(**kwargs) -> MenuItem:
|
||||
params = {"hook_hash": "hook_hash"}
|
||||
params.update(kwargs)
|
||||
return _create_menu_item(**params)
|
||||
|
||||
|
||||
def create_folder_menu_item(**kwargs) -> MenuItem:
|
||||
return _create_menu_item(**kwargs)
|
||||
|
||||
|
||||
def create_menu_item_from_hook(hook, **kwargs) -> MenuItem:
|
||||
item = hook()
|
||||
hook_params = menu_item_hooks.gather_params(item)
|
||||
params = {
|
||||
"text": hook_params.text,
|
||||
"hook_hash": hook_params.hash,
|
||||
"order": hook_params.order,
|
||||
}
|
||||
params.update(kwargs)
|
||||
return _create_menu_item(**params)
|
||||
|
||||
|
||||
def _create_menu_item(**kwargs) -> MenuItem:
|
||||
num = next(counter_menu_item)
|
||||
params = {
|
||||
"text": f"text #{num}",
|
||||
}
|
||||
params.update(kwargs)
|
||||
return MenuItem.objects.create(**params)
|
||||
|
||||
|
||||
def create_rendered_menu_item(**kwargs) -> RenderedMenuItem:
|
||||
if "menu_item" not in kwargs:
|
||||
kwargs["menu_item"] = create_link_menu_item()
|
||||
|
||||
return RenderedMenuItem(**kwargs)
|
||||
|
||||
|
||||
counter_menu_item = count(1, 1)
|
||||
counter_menu_item_hook = count(1, 1)
|
||||
counter_user = count(1, 1)
|
0
allianceauth/menu/tests/integration/__init__.py
Normal file
0
allianceauth/menu/tests/integration/__init__.py
Normal file
178
allianceauth/menu/tests/integration/test_admin.py
Normal file
178
allianceauth/menu/tests/integration/test_admin.py
Normal file
@ -0,0 +1,178 @@
|
||||
from http import HTTPStatus
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from allianceauth.menu.constants import MenuItemType
|
||||
from allianceauth.menu.forms import (
|
||||
AppMenuItemAdminForm,
|
||||
FolderMenuItemAdminForm,
|
||||
LinkMenuItemAdminForm,
|
||||
)
|
||||
from allianceauth.menu.models import MenuItem
|
||||
from allianceauth.menu.tests.factories import (
|
||||
create_app_menu_item,
|
||||
create_folder_menu_item,
|
||||
create_link_menu_item,
|
||||
create_user,
|
||||
)
|
||||
from allianceauth.menu.tests.utils import extract_html
|
||||
|
||||
|
||||
def extract_menu_item_texts(response):
|
||||
"""Extract labels of menu items shown in change list."""
|
||||
soup = extract_html(response)
|
||||
items = soup.find_all("th", {"class": "field-_text"})
|
||||
labels = {elem.text for elem in items}
|
||||
return labels
|
||||
|
||||
|
||||
class TestAdminSite(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.user = create_user(is_superuser=True, is_staff=True)
|
||||
cls.changelist_url = reverse("admin:menu_menuitem_changelist")
|
||||
cls.add_url = reverse("admin:menu_menuitem_add")
|
||||
|
||||
def change_url(self, id_):
|
||||
return reverse("admin:menu_menuitem_change", args=[id_])
|
||||
|
||||
def test_changelist_should_show_all_types(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
create_app_menu_item(text="app")
|
||||
create_folder_menu_item(text="folder")
|
||||
create_link_menu_item(text="link")
|
||||
|
||||
# when
|
||||
response = self.client.get(self.changelist_url)
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||
labels = extract_menu_item_texts(response)
|
||||
self.assertSetEqual(labels, {"app", "[folder]", "link"})
|
||||
|
||||
def test_should_create_new_link_item(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
|
||||
# when
|
||||
response = self.client.post(
|
||||
self.add_url,
|
||||
{"text": "alpha", "url": "http://www.example.com", "order": 99},
|
||||
)
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
||||
self.assertEqual(response.url, self.changelist_url)
|
||||
self.assertEqual(MenuItem.objects.count(), 1)
|
||||
obj = MenuItem.objects.first()
|
||||
self.assertEqual(obj.text, "alpha")
|
||||
self.assertEqual(obj.item_type, MenuItemType.LINK)
|
||||
|
||||
def test_should_create_new_folder_item(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
|
||||
# when
|
||||
response = self.client.post(
|
||||
self.add_url + "?type=folder", {"text": "alpha", "order": 99}
|
||||
)
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
||||
self.assertEqual(response.url, self.changelist_url)
|
||||
self.assertEqual(MenuItem.objects.count(), 1)
|
||||
obj = MenuItem.objects.first()
|
||||
self.assertEqual(obj.text, "alpha")
|
||||
self.assertEqual(obj.item_type, MenuItemType.FOLDER)
|
||||
|
||||
def test_should_change_app_item(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
item = create_app_menu_item(text="alpha", order=1)
|
||||
form_data = AppMenuItemAdminForm(instance=item).initial
|
||||
form_data["order"] = 99
|
||||
form_data["parent"] = ""
|
||||
|
||||
# when
|
||||
response = self.client.post(self.change_url(item.id), form_data)
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
||||
self.assertEqual(response.url, self.changelist_url)
|
||||
self.assertEqual(MenuItem.objects.count(), 1)
|
||||
obj = MenuItem.objects.first()
|
||||
self.assertEqual(obj.order, 99)
|
||||
self.assertEqual(obj.item_type, MenuItemType.APP)
|
||||
|
||||
def test_should_change_link_item(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
item = create_link_menu_item(text="alpha")
|
||||
form_data = LinkMenuItemAdminForm(instance=item).initial
|
||||
form_data["text"] = "bravo"
|
||||
form_data["parent"] = ""
|
||||
|
||||
# when
|
||||
response = self.client.post(self.change_url(item.id), form_data)
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
||||
self.assertEqual(response.url, self.changelist_url)
|
||||
self.assertEqual(MenuItem.objects.count(), 1)
|
||||
obj = MenuItem.objects.first()
|
||||
self.assertEqual(obj.text, "bravo")
|
||||
self.assertEqual(obj.item_type, MenuItemType.LINK)
|
||||
|
||||
def test_should_change_folder_item(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
item = create_folder_menu_item(text="alpha")
|
||||
form_data = FolderMenuItemAdminForm(instance=item).initial
|
||||
form_data["text"] = "bravo"
|
||||
|
||||
# when
|
||||
response = self.client.post(self.change_url(item.id), form_data)
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
||||
self.assertEqual(response.url, self.changelist_url)
|
||||
self.assertEqual(MenuItem.objects.count(), 1)
|
||||
obj = MenuItem.objects.first()
|
||||
self.assertEqual(obj.text, "bravo")
|
||||
self.assertEqual(obj.item_type, MenuItemType.FOLDER)
|
||||
|
||||
def test_should_move_item_into_folder(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
link = create_link_menu_item(text="alpha")
|
||||
folder = create_folder_menu_item(text="folder")
|
||||
form_data = LinkMenuItemAdminForm(instance=link).initial
|
||||
form_data["parent"] = folder.id
|
||||
|
||||
# when
|
||||
response = self.client.post(self.change_url(link.id), form_data)
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
||||
self.assertEqual(response.url, self.changelist_url)
|
||||
link.refresh_from_db()
|
||||
self.assertEqual(link.parent, folder)
|
||||
|
||||
def test_should_filter_items_by_type(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
create_app_menu_item(text="app")
|
||||
create_folder_menu_item(text="folder")
|
||||
create_link_menu_item(text="link")
|
||||
|
||||
# when
|
||||
cases = [("link", "link"), ("app", "app"), ("folder", "[folder]")]
|
||||
for filter_name, expected_label in cases:
|
||||
with self.subTest(filter_name=filter_name):
|
||||
response = self.client.get(self.changelist_url + f"?type={filter_name}")
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||
labels = extract_menu_item_texts(response)
|
||||
self.assertSetEqual(labels, {expected_label})
|
102
allianceauth/menu/tests/integration/test_dashboard.py
Normal file
102
allianceauth/menu/tests/integration/test_dashboard.py
Normal file
@ -0,0 +1,102 @@
|
||||
from http import HTTPStatus
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.menu.core.smart_sync import reset_menu_items_sync
|
||||
from allianceauth.menu.tests.factories import (
|
||||
create_folder_menu_item,
|
||||
create_link_menu_item,
|
||||
create_user,
|
||||
)
|
||||
from allianceauth.menu.tests.utils import extract_links
|
||||
|
||||
|
||||
class TestDefaultDashboardWithSideMenu(TestCase):
|
||||
def test_should_show_all_types_of_menu_entries(self):
|
||||
# given
|
||||
user = create_user(permissions=["auth.group_management"])
|
||||
self.client.force_login(user)
|
||||
create_link_menu_item(text="Alpha", url="http://www.example.com/alpha")
|
||||
folder = create_folder_menu_item(text="Folder")
|
||||
create_link_menu_item(
|
||||
text="Bravo", url="http://www.example.com/bravo", parent=folder
|
||||
)
|
||||
reset_menu_items_sync() # this simulates startup
|
||||
|
||||
# when
|
||||
response = self.client.get("/dashboard/")
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||
links = extract_links(response)
|
||||
# open_page_in_browser(response)
|
||||
self.assertEqual(links["/dashboard/"], "Dashboard")
|
||||
self.assertEqual(links["/groups/"], "Groups")
|
||||
self.assertEqual(links["/groupmanagement/requests/"], "Group Management")
|
||||
self.assertEqual(links["http://www.example.com/alpha"], "Alpha")
|
||||
self.assertEqual(links["http://www.example.com/bravo"], "Bravo")
|
||||
|
||||
def test_should_not_show_menu_entry_when_user_has_no_permission(self):
|
||||
# given
|
||||
user = create_user()
|
||||
self.client.force_login(user)
|
||||
reset_menu_items_sync()
|
||||
|
||||
# when
|
||||
response = self.client.get("/dashboard/")
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||
links = extract_links(response)
|
||||
self.assertEqual(links["/dashboard/"], "Dashboard")
|
||||
self.assertEqual(links["/groups/"], "Groups")
|
||||
self.assertNotIn("/groupmanagement/requests/", links)
|
||||
|
||||
def test_should_not_show_menu_entry_when_hidden(self):
|
||||
# given
|
||||
user = create_user()
|
||||
self.client.force_login(user)
|
||||
create_link_menu_item(text="Alpha", url="http://www.example.com/")
|
||||
reset_menu_items_sync()
|
||||
|
||||
# when
|
||||
response = self.client.get("/dashboard/")
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||
links = extract_links(response)
|
||||
self.assertEqual(links["/dashboard/"], "Dashboard")
|
||||
self.assertEqual(links["/groups/"], "Groups")
|
||||
self.assertNotIn("http://www.example.com/alpha", links)
|
||||
|
||||
|
||||
class TestBS3DashboardWithSideMenu(TestCase):
|
||||
def test_should_not_show_group_management_when_user_has_no_permission(self):
|
||||
# given
|
||||
user = create_user()
|
||||
self.client.force_login(user)
|
||||
|
||||
# when
|
||||
response = self.client.get("/dashboard_bs3/")
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||
links = extract_links(response)
|
||||
self.assertEqual(links["/dashboard/"], "Dashboard")
|
||||
self.assertEqual(links["/groups/"], "Groups")
|
||||
self.assertNotIn("/groupmanagement/requests/", links)
|
||||
|
||||
def test_should_show_group_management_when_user_has_permission(self):
|
||||
# given
|
||||
user = create_user(permissions=["auth.group_management"])
|
||||
self.client.force_login(user)
|
||||
|
||||
# when
|
||||
response = self.client.get("/dashboard_bs3/")
|
||||
|
||||
# then
|
||||
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||
links = extract_links(response)
|
||||
self.assertEqual(links["/dashboard/"], "Dashboard")
|
||||
self.assertEqual(links["/groups/"], "Groups")
|
||||
self.assertEqual(links["/groupmanagement/requests/"], "Group Management")
|
0
allianceauth/menu/tests/templatetags/__init__.py
Normal file
0
allianceauth/menu/tests/templatetags/__init__.py
Normal file
54
allianceauth/menu/tests/templatetags/test_menu_items.py
Normal file
54
allianceauth/menu/tests/templatetags/test_menu_items.py
Normal file
@ -0,0 +1,54 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from allianceauth.menu.templatetags.menu_items import render_menu
|
||||
from allianceauth.menu.tests.factories import create_menu_item_hook_function
|
||||
from allianceauth.menu.tests.utils import PACKAGE_PATH, render_template
|
||||
|
||||
MODULE_PATH = PACKAGE_PATH + ".templatetags.menu_items"
|
||||
|
||||
|
||||
class TestTemplateTags(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
|
||||
@patch(MODULE_PATH + ".render_menu", spec=True)
|
||||
def test_menu_items(self, mock_render_menu):
|
||||
# given
|
||||
mock_render_menu.return_value = ["Alpha"]
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
rendered = render_template(
|
||||
"{% load menu_items %}{% menu_items %}",
|
||||
context={"request": request},
|
||||
)
|
||||
self.assertIn("Alpha", rendered)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + ".get_hooks", spec=True)
|
||||
class TestRenderMenu(TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def test_should_render_menu_in_order(self, mock_get_hooks):
|
||||
# given
|
||||
mock_get_hooks.return_value = [
|
||||
create_menu_item_hook_function(text="Charlie"),
|
||||
create_menu_item_hook_function(text="Alpha", order=1),
|
||||
create_menu_item_hook_function(text="Bravo", order=2),
|
||||
]
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 3)
|
||||
self.assertIn("Alpha", menu[0])
|
||||
self.assertIn("Bravo", menu[1])
|
||||
self.assertIn("Charlie", menu[2])
|
326
allianceauth/menu/tests/templatetags/test_menu_menu_items.py
Normal file
326
allianceauth/menu/tests/templatetags/test_menu_menu_items.py
Normal file
@ -0,0 +1,326 @@
|
||||
from typing import List, NamedTuple, Optional
|
||||
from unittest.mock import patch
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from allianceauth.menu.templatetags.menu_menu_items import (
|
||||
RenderedMenuItem,
|
||||
render_menu,
|
||||
)
|
||||
from allianceauth.menu.tests.factories import (
|
||||
create_app_menu_item,
|
||||
create_folder_menu_item,
|
||||
create_link_menu_item,
|
||||
create_menu_item_from_hook,
|
||||
create_menu_item_hook_function,
|
||||
create_rendered_menu_item,
|
||||
)
|
||||
from allianceauth.menu.tests.utils import (
|
||||
PACKAGE_PATH,
|
||||
remove_whitespaces,
|
||||
render_template,
|
||||
)
|
||||
|
||||
MODULE_PATH = PACKAGE_PATH + ".templatetags.menu_menu_items"
|
||||
|
||||
|
||||
class TestTemplateTags(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
|
||||
@patch(MODULE_PATH + ".render_menu", spec=True)
|
||||
@patch(MODULE_PATH + ".smart_sync.sync_menu", spec=True)
|
||||
def test_sorted_menu_items(self, mock_sync_menu, mock_render_menu):
|
||||
# given
|
||||
fake_item = {"html": "Alpha"}
|
||||
mock_render_menu.return_value = [fake_item]
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
rendered = render_template(
|
||||
"{% load menu_menu_items %}{% menu_items %}",
|
||||
context={"request": request},
|
||||
)
|
||||
self.assertIn("Alpha", rendered)
|
||||
self.assertTrue(mock_sync_menu.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + ".get_hooks", spec=True)
|
||||
class TestRenderDefaultMenu(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
|
||||
def test_should_render_app_menu_items(self, mock_get_hooks):
|
||||
# given
|
||||
menu = [
|
||||
create_menu_item_hook_function(text="Charlie", count=42),
|
||||
create_menu_item_hook_function(text="Alpha", order=1),
|
||||
create_menu_item_hook_function(text="Bravo", order=2),
|
||||
]
|
||||
mock_get_hooks.return_value = menu
|
||||
for hook in menu:
|
||||
create_menu_item_from_hook(hook)
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 3)
|
||||
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
||||
self.assertEqual(menu[1].menu_item.text, "Bravo")
|
||||
self.assertEqual(menu[2].menu_item.text, "Charlie")
|
||||
self.assertEqual(menu[2].count, 42)
|
||||
attrs = parse_html(menu[2])
|
||||
self.assertEqual(attrs.count, 42)
|
||||
self.assertEqual(attrs.text, "Charlie")
|
||||
|
||||
def test_should_render_link_menu_items(self, mock_get_hooks):
|
||||
# given
|
||||
mock_get_hooks.return_value = []
|
||||
create_link_menu_item(text="Charlie"),
|
||||
create_link_menu_item(text="Alpha", order=1),
|
||||
create_link_menu_item(text="Bravo", order=2),
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 3)
|
||||
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
||||
self.assertEqual(menu[1].menu_item.text, "Bravo")
|
||||
self.assertEqual(menu[2].menu_item.text, "Charlie")
|
||||
attrs = parse_html(menu[2])
|
||||
self.assertEqual(attrs.text, "Charlie")
|
||||
|
||||
def test_should_render_folders(self, mock_get_hooks):
|
||||
# given
|
||||
mock_get_hooks.return_value = []
|
||||
folder = create_folder_menu_item(text="Folder", order=2)
|
||||
create_link_menu_item(text="Alpha", order=1)
|
||||
create_link_menu_item(text="Bravo", order=3)
|
||||
create_link_menu_item(text="Charlie", parent=folder)
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 3)
|
||||
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
||||
self.assertEqual(menu[1].menu_item.text, "Folder")
|
||||
self.assertEqual(menu[2].menu_item.text, "Bravo")
|
||||
|
||||
self.assertEqual(menu[1].children[0].menu_item.text, "Charlie")
|
||||
attrs = parse_html(menu[1].children[0])
|
||||
self.assertEqual(attrs.text, "Charlie")
|
||||
|
||||
def test_should_render_folder_properties(self, mock_get_hooks):
|
||||
# given
|
||||
# given
|
||||
menu = [
|
||||
create_menu_item_hook_function(text="Charlie", count=42),
|
||||
create_menu_item_hook_function(text="Alpha", count=5),
|
||||
create_menu_item_hook_function(text="Bravo"),
|
||||
]
|
||||
mock_get_hooks.return_value = menu
|
||||
|
||||
folder = create_folder_menu_item(text="Folder", order=1)
|
||||
for hook in menu:
|
||||
create_menu_item_from_hook(hook, parent=folder)
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 1)
|
||||
item = menu[0]
|
||||
self.assertEqual(item.menu_item.text, "Folder")
|
||||
self.assertEqual(item.count, 47)
|
||||
self.assertTrue(item.is_folder)
|
||||
self.assertEqual(len(item.children), 3)
|
||||
attrs = parse_html(item)
|
||||
self.assertEqual(attrs.count, 47)
|
||||
self.assertIn("fa-folder", attrs.classes)
|
||||
|
||||
def test_should_remove_empty_folders(self, mock_get_hooks):
|
||||
# given
|
||||
mock_get_hooks.return_value = []
|
||||
create_folder_menu_item(text="Folder", order=2)
|
||||
create_link_menu_item(text="Alpha", order=1)
|
||||
create_link_menu_item(text="Bravo", order=3)
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 2)
|
||||
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
||||
self.assertEqual(menu[1].menu_item.text, "Bravo")
|
||||
|
||||
def test_should_not_include_hidden_items(self, mock_get_hooks):
|
||||
# given
|
||||
mock_get_hooks.return_value = []
|
||||
create_link_menu_item(text="Charlie"),
|
||||
create_link_menu_item(text="Alpha", order=1),
|
||||
create_link_menu_item(text="Bravo", order=2, is_hidden=True),
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 2)
|
||||
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
||||
self.assertEqual(menu[1].menu_item.text, "Charlie")
|
||||
|
||||
def test_should_not_render_hidden_folders(self, mock_get_hooks):
|
||||
# given
|
||||
# given
|
||||
menu = [
|
||||
create_menu_item_hook_function(text="Charlie", count=42),
|
||||
create_menu_item_hook_function(text="Alpha", count=5),
|
||||
create_menu_item_hook_function(text="Bravo"),
|
||||
]
|
||||
mock_get_hooks.return_value = menu
|
||||
|
||||
folder = create_folder_menu_item(text="Folder", order=1, is_hidden=True)
|
||||
for hook in menu:
|
||||
create_menu_item_from_hook(hook, parent=folder)
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 0)
|
||||
|
||||
def test_should_allow_several_items_with_same_text(self, mock_get_hooks):
|
||||
# given
|
||||
mock_get_hooks.return_value = []
|
||||
create_link_menu_item(text="Alpha", order=1),
|
||||
create_link_menu_item(text="Alpha", order=2),
|
||||
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
result = render_menu(request)
|
||||
|
||||
# then
|
||||
menu = list(result)
|
||||
self.assertEqual(len(menu), 2)
|
||||
self.assertEqual(menu[0].menu_item.text, "Alpha")
|
||||
self.assertEqual(menu[1].menu_item.text, "Alpha")
|
||||
|
||||
|
||||
class TestRenderedMenuItem(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
cls.template = "menu/menu-item-bs5.html"
|
||||
|
||||
def test_create_from_menu_item_with_defaults(self):
|
||||
# given
|
||||
item = create_link_menu_item()
|
||||
|
||||
# when
|
||||
obj = RenderedMenuItem(menu_item=item)
|
||||
|
||||
# then
|
||||
self.assertEqual(obj.menu_item, item)
|
||||
self.assertIsNone(obj.count)
|
||||
self.assertEqual(obj.html, "")
|
||||
self.assertEqual(obj.html_id, "")
|
||||
self.assertListEqual(obj.children, [])
|
||||
|
||||
def test_should_identify_if_item_is_a_folder(self):
|
||||
# given
|
||||
app_item = create_rendered_menu_item(menu_item=create_app_menu_item())
|
||||
link_item = create_rendered_menu_item(menu_item=create_link_menu_item())
|
||||
folder_item = create_rendered_menu_item(menu_item=create_folder_menu_item())
|
||||
|
||||
cases = [
|
||||
(app_item, False),
|
||||
(link_item, False),
|
||||
(folder_item, True),
|
||||
]
|
||||
# when
|
||||
for obj, expected in cases:
|
||||
with self.subTest(type=expected):
|
||||
self.assertIs(obj.is_folder, expected)
|
||||
|
||||
def test_should_update_html_for_link_item(self):
|
||||
# given
|
||||
obj = create_rendered_menu_item(menu_item=create_link_menu_item(text="Alpha"))
|
||||
request = self.factory.get("/")
|
||||
|
||||
# when
|
||||
obj.update_html(request, self.template)
|
||||
|
||||
# then
|
||||
parsed = parse_html(obj)
|
||||
self.assertEqual(parsed.text, "Alpha")
|
||||
self.assertIsNone(parsed.count)
|
||||
self.assertFalse(obj.html_id)
|
||||
|
||||
def test_should_update_html_for_folder_item(self):
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
folder_item = create_folder_menu_item(text="Alpha")
|
||||
link_item = create_link_menu_item(text="Bravo", parent=folder_item)
|
||||
obj = create_rendered_menu_item(menu_item=folder_item, count=42)
|
||||
rendered_link = create_rendered_menu_item(menu_item=link_item)
|
||||
rendered_link.update_html(request, self.template)
|
||||
obj.children.append(rendered_link)
|
||||
|
||||
# when
|
||||
obj.update_html(request, self.template)
|
||||
|
||||
# then
|
||||
self.assertTrue(obj.html_id)
|
||||
parsed_parent = parse_html(obj)
|
||||
self.assertEqual(parsed_parent.text, "Alpha")
|
||||
self.assertEqual(parsed_parent.count, 42)
|
||||
self.assertIn("Bravo", obj.html)
|
||||
|
||||
|
||||
class _ParsedMenuItem(NamedTuple):
|
||||
classes: List[str]
|
||||
text: str
|
||||
count: Optional[int]
|
||||
|
||||
|
||||
def parse_html(obj: RenderedMenuItem) -> _ParsedMenuItem:
|
||||
soup = BeautifulSoup(obj.html, "html.parser")
|
||||
classes = soup.li.i.attrs["class"]
|
||||
text = remove_whitespaces(soup.li.a.text)
|
||||
try:
|
||||
count = int(remove_whitespaces(soup.li.span.text))
|
||||
except (AttributeError, ValueError):
|
||||
count = None
|
||||
|
||||
return _ParsedMenuItem(classes=classes, text=text, count=count)
|
28
allianceauth/menu/tests/test_forms.py
Normal file
28
allianceauth/menu/tests/test_forms.py
Normal file
@ -0,0 +1,28 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.menu.constants import DEFAULT_FOLDER_ICON_CLASSES
|
||||
from allianceauth.menu.forms import FolderMenuItemAdminForm
|
||||
|
||||
|
||||
class TestFolderMenuItemAdminForm(TestCase):
|
||||
def test_should_set_default_icon_classes(self):
|
||||
# given
|
||||
form_data = {"text": "Alpha", "order": 1}
|
||||
form = FolderMenuItemAdminForm(data=form_data)
|
||||
|
||||
# when
|
||||
obj = form.save(commit=False)
|
||||
|
||||
# then
|
||||
self.assertEqual(obj.classes, DEFAULT_FOLDER_ICON_CLASSES)
|
||||
|
||||
def test_should_use_icon_classes_from_input(self):
|
||||
# given
|
||||
form_data = {"text": "Alpha", "order": 1, "classes": "dummy"}
|
||||
form = FolderMenuItemAdminForm(data=form_data)
|
||||
|
||||
# when
|
||||
obj = form.save(commit=False)
|
||||
|
||||
# then
|
||||
self.assertEqual(obj.classes, "dummy")
|
82
allianceauth/menu/tests/test_hooks.py
Normal file
82
allianceauth/menu/tests/test_hooks.py
Normal file
@ -0,0 +1,82 @@
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from allianceauth.menu.hooks import MenuItemHook
|
||||
|
||||
from .factories import create_menu_item_hook
|
||||
|
||||
|
||||
class TestMenuItemHook(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.factory = RequestFactory()
|
||||
|
||||
def test_should_create_obj_with_minimal_params(self):
|
||||
# when
|
||||
obj = MenuItemHook("text", "classes", "url-name")
|
||||
|
||||
# then
|
||||
self.assertEqual(obj.text, "text")
|
||||
self.assertEqual(obj.classes, "classes")
|
||||
self.assertEqual(obj.url_name, "url-name")
|
||||
self.assertEqual(obj.template, "public/menuitem.html")
|
||||
self.assertEqual(obj.order, 9999)
|
||||
self.assertListEqual(obj.navactive, ["url-name"])
|
||||
self.assertIsNone(obj.count)
|
||||
|
||||
def test_should_create_obj_with_full_params_1(self):
|
||||
# when
|
||||
obj = MenuItemHook("text", "classes", "url-name", 5, ["navactive"])
|
||||
|
||||
# then
|
||||
self.assertEqual(obj.text, "text")
|
||||
self.assertEqual(obj.classes, "classes")
|
||||
self.assertEqual(obj.url_name, "url-name")
|
||||
self.assertEqual(obj.template, "public/menuitem.html")
|
||||
self.assertEqual(obj.order, 5)
|
||||
self.assertListEqual(obj.navactive, ["navactive", "url-name"])
|
||||
self.assertIsNone(obj.count)
|
||||
|
||||
def test_should_create_obj_with_full_params_2(self):
|
||||
# when
|
||||
obj = MenuItemHook(
|
||||
text="text",
|
||||
classes="classes",
|
||||
url_name="url-name",
|
||||
order=5,
|
||||
navactive=["navactive"],
|
||||
)
|
||||
|
||||
# then
|
||||
self.assertEqual(obj.text, "text")
|
||||
self.assertEqual(obj.classes, "classes")
|
||||
self.assertEqual(obj.url_name, "url-name")
|
||||
self.assertEqual(obj.template, "public/menuitem.html")
|
||||
self.assertEqual(obj.order, 5)
|
||||
self.assertListEqual(obj.navactive, ["navactive", "url-name"])
|
||||
self.assertIsNone(obj.count)
|
||||
|
||||
def test_should_render_menu_item(self):
|
||||
# given
|
||||
request = self.factory.get("/")
|
||||
hook = create_menu_item_hook(text="Alpha")
|
||||
|
||||
# when
|
||||
result = hook.render(request)
|
||||
|
||||
# then
|
||||
self.assertIn("Alpha", result)
|
||||
|
||||
def test_str(self):
|
||||
# given
|
||||
hook = create_menu_item_hook(text="Alpha")
|
||||
|
||||
# when/then
|
||||
self.assertEqual(str(hook), "Alpha")
|
||||
|
||||
def test_repr(self):
|
||||
# given
|
||||
hook = create_menu_item_hook(text="Alpha")
|
||||
|
||||
# when/then
|
||||
self.assertIn("Alpha", repr(hook))
|
103
allianceauth/menu/tests/test_managers.py
Normal file
103
allianceauth/menu/tests/test_managers.py
Normal file
@ -0,0 +1,103 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.db.models import QuerySet
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.menu.constants import MenuItemType
|
||||
from allianceauth.menu.models import MenuItem
|
||||
|
||||
from .factories import (
|
||||
create_app_menu_item,
|
||||
create_folder_menu_item,
|
||||
create_link_menu_item,
|
||||
create_menu_item_from_hook,
|
||||
create_menu_item_hook_function,
|
||||
)
|
||||
from .utils import PACKAGE_PATH
|
||||
|
||||
|
||||
class TestMenuItemQuerySet(TestCase):
|
||||
def test_should_add_item_type_field(self):
|
||||
# given
|
||||
app_item = create_app_menu_item()
|
||||
link_item = create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
# when
|
||||
result: QuerySet[MenuItem] = MenuItem.objects.annotate_item_type_2()
|
||||
|
||||
# then
|
||||
for obj in [app_item, link_item, folder_item]:
|
||||
obj = result.get(pk=app_item.pk)
|
||||
self.assertEqual(obj.item_type_2, obj.item_type)
|
||||
|
||||
def test_should_filter_folders(self):
|
||||
# given
|
||||
create_app_menu_item()
|
||||
create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
# when
|
||||
result: QuerySet[MenuItem] = MenuItem.objects.filter_folders()
|
||||
|
||||
# then
|
||||
item_pks = set(result.values_list("pk", flat=True))
|
||||
self.assertSetEqual(item_pks, {folder_item.pk})
|
||||
|
||||
|
||||
@patch(PACKAGE_PATH + ".managers.get_hooks", spec=True)
|
||||
class TestMenuItemManagerSyncAll(TestCase):
|
||||
def test_should_create_new_items_from_hooks_when_they_do_not_exist(
|
||||
self, mock_get_hooks
|
||||
):
|
||||
# given
|
||||
mock_get_hooks.return_value = [create_menu_item_hook_function(text="Alpha")]
|
||||
|
||||
# when
|
||||
MenuItem.objects.sync_all()
|
||||
|
||||
# then
|
||||
self.assertEqual(MenuItem.objects.count(), 1)
|
||||
obj = MenuItem.objects.first()
|
||||
self.assertEqual(obj.item_type, MenuItemType.APP)
|
||||
self.assertEqual(obj.text, "Alpha")
|
||||
|
||||
def test_should_update_existing_app_items_when_changed_only(self, mock_get_hooks):
|
||||
# given
|
||||
menu_hook_1 = create_menu_item_hook_function(text="Alpha", order=1)
|
||||
menu_hook_2 = create_menu_item_hook_function(text="Bravo", order=2)
|
||||
mock_get_hooks.return_value = [menu_hook_1, menu_hook_2]
|
||||
create_menu_item_from_hook(menu_hook_1, text="name has changed", order=99)
|
||||
create_menu_item_from_hook(menu_hook_2)
|
||||
|
||||
# when
|
||||
MenuItem.objects.sync_all()
|
||||
|
||||
# then
|
||||
self.assertEqual(MenuItem.objects.count(), 2)
|
||||
|
||||
obj = MenuItem.objects.get(text="Alpha")
|
||||
self.assertEqual(obj.item_type, MenuItemType.APP)
|
||||
self.assertEqual(obj.order, 99)
|
||||
|
||||
obj = MenuItem.objects.get(text="Bravo")
|
||||
self.assertEqual(obj.item_type, MenuItemType.APP)
|
||||
self.assertEqual(obj.order, 2)
|
||||
|
||||
def test_should_remove_obsolete_app_items_but_keep_user_items(self, mock_get_hooks):
|
||||
# given
|
||||
menu_hook = create_menu_item_hook_function(text="Alpha")
|
||||
mock_get_hooks.return_value = [menu_hook]
|
||||
create_app_menu_item(text="Bravo") # obsolete item
|
||||
link_item = create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
# when
|
||||
MenuItem.objects.sync_all()
|
||||
|
||||
# then
|
||||
self.assertEqual(MenuItem.objects.count(), 3)
|
||||
obj = MenuItem.objects.get(text="Alpha")
|
||||
self.assertTrue(obj.item_type, MenuItemType.APP)
|
||||
self.assertIn(link_item, MenuItem.objects.all())
|
||||
self.assertIn(folder_item, MenuItem.objects.all())
|
166
allianceauth/menu/tests/test_models.py
Normal file
166
allianceauth/menu/tests/test_models.py
Normal file
@ -0,0 +1,166 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from allianceauth.menu.constants import MenuItemType
|
||||
|
||||
from .factories import (
|
||||
create_app_menu_item,
|
||||
create_folder_menu_item,
|
||||
create_link_menu_item,
|
||||
)
|
||||
|
||||
|
||||
class TestMenuItem(TestCase):
|
||||
def test_str(self):
|
||||
# given
|
||||
obj = create_link_menu_item()
|
||||
# when
|
||||
result = str(obj)
|
||||
# then
|
||||
self.assertIsInstance(result, str)
|
||||
|
||||
def test_should_return_item_type(self):
|
||||
# given
|
||||
app_item = create_app_menu_item()
|
||||
link_item = create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
cases = [
|
||||
(app_item, MenuItemType.APP),
|
||||
(link_item, MenuItemType.LINK),
|
||||
(folder_item, MenuItemType.FOLDER),
|
||||
]
|
||||
# when
|
||||
for obj, expected in cases:
|
||||
with self.subTest(type=expected):
|
||||
self.assertEqual(obj.item_type, expected)
|
||||
|
||||
def test_should_identify_if_item_is_a_child(self):
|
||||
# given
|
||||
folder = create_folder_menu_item()
|
||||
child = create_link_menu_item(parent=folder)
|
||||
not_child = create_link_menu_item()
|
||||
|
||||
cases = [(child, True), (not_child, False)]
|
||||
# when
|
||||
for obj, expected in cases:
|
||||
with self.subTest(type=expected):
|
||||
self.assertIs(obj.is_child, expected)
|
||||
|
||||
def test_should_identify_if_item_is_a_folder(self):
|
||||
# given
|
||||
app_item = create_app_menu_item()
|
||||
link_item = create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
cases = [
|
||||
(app_item, False),
|
||||
(link_item, False),
|
||||
(folder_item, True),
|
||||
]
|
||||
# when
|
||||
for obj, expected in cases:
|
||||
with self.subTest(type=expected):
|
||||
self.assertIs(obj.is_folder, expected)
|
||||
|
||||
def test_should_identify_if_item_is_user_defined(self):
|
||||
# given
|
||||
app_item = create_app_menu_item()
|
||||
link_item = create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
cases = [
|
||||
(app_item, False),
|
||||
(link_item, True),
|
||||
(folder_item, True),
|
||||
]
|
||||
# when
|
||||
for obj, expected in cases:
|
||||
with self.subTest(type=expected):
|
||||
self.assertIs(obj.is_user_defined, expected)
|
||||
|
||||
def test_should_identify_if_item_is_an_app_item(self):
|
||||
# given
|
||||
app_item = create_app_menu_item()
|
||||
link_item = create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
cases = [
|
||||
(app_item, True),
|
||||
(link_item, False),
|
||||
(folder_item, False),
|
||||
]
|
||||
# when
|
||||
for obj, expected in cases:
|
||||
with self.subTest(type=expected):
|
||||
self.assertIs(obj.is_app_item, expected)
|
||||
|
||||
def test_should_identify_if_item_is_a_link_item(self):
|
||||
# given
|
||||
app_item = create_app_menu_item()
|
||||
link_item = create_link_menu_item()
|
||||
folder_item = create_folder_menu_item()
|
||||
|
||||
cases = [
|
||||
(app_item, False),
|
||||
(link_item, True),
|
||||
(folder_item, False),
|
||||
]
|
||||
# when
|
||||
for obj, expected in cases:
|
||||
with self.subTest(type=expected):
|
||||
self.assertIs(obj.is_link_item, expected)
|
||||
|
||||
def test_should_not_allow_creating_invalid_app_item(self):
|
||||
# when
|
||||
obj = create_app_menu_item(hook_hash="")
|
||||
|
||||
# then
|
||||
obj.refresh_from_db()
|
||||
self.assertIsNone(obj.hook_hash)
|
||||
|
||||
|
||||
class TestMenuItemToHookObj(TestCase):
|
||||
def test_should_create_from_link_item(self):
|
||||
# given
|
||||
obj = create_link_menu_item(text="Alpha")
|
||||
|
||||
# when
|
||||
hook_obj = obj.to_hook_obj()
|
||||
|
||||
# then
|
||||
self.assertEqual(hook_obj.text, "Alpha")
|
||||
self.assertEqual(hook_obj.url, obj.url)
|
||||
self.assertEqual(hook_obj.html_id, "")
|
||||
self.assertFalse(hook_obj.is_folder)
|
||||
|
||||
def test_should_create_from_folder(self):
|
||||
# given
|
||||
obj = create_folder_menu_item(text="Alpha", classes="dummy")
|
||||
|
||||
# when
|
||||
hook_obj = obj.to_hook_obj()
|
||||
|
||||
# then
|
||||
self.assertEqual(hook_obj.text, "Alpha")
|
||||
self.assertEqual(hook_obj.classes, "dummy")
|
||||
self.assertEqual(hook_obj.url, "")
|
||||
self.assertTrue(hook_obj.html_id)
|
||||
self.assertTrue(hook_obj.is_folder)
|
||||
|
||||
def test_should_create_from_folder_and_use_default_icon_classes(self):
|
||||
# given
|
||||
obj = create_folder_menu_item(classes="")
|
||||
|
||||
# when
|
||||
hook_obj = obj.to_hook_obj()
|
||||
|
||||
# then
|
||||
self.assertEqual(hook_obj.classes, "fa-solid fa-folder")
|
||||
|
||||
def test_should_create_from_app_item(self):
|
||||
# given
|
||||
obj = create_app_menu_item(text="Alpha")
|
||||
|
||||
# when
|
||||
with self.assertRaises(ValueError):
|
||||
obj.to_hook_obj()
|
47
allianceauth/menu/tests/utils.py
Normal file
47
allianceauth/menu/tests/utils.py
Normal file
@ -0,0 +1,47 @@
|
||||
import tempfile
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.template import Context, Template
|
||||
|
||||
PACKAGE_PATH = "allianceauth.menu"
|
||||
|
||||
|
||||
def extract_links(response: HttpResponse) -> dict:
|
||||
soup = extract_html(response)
|
||||
links = {
|
||||
link["href"]: "".join(link.stripped_strings)
|
||||
for link in soup.find_all("a", href=True)
|
||||
}
|
||||
return links
|
||||
|
||||
|
||||
def extract_html(response: HttpResponse) -> BeautifulSoup:
|
||||
soup = BeautifulSoup(response.content, "html.parser")
|
||||
return soup
|
||||
|
||||
|
||||
def open_page_in_browser(response: HttpResponse):
|
||||
"""Open the response in the system's default browser.
|
||||
|
||||
This will create a temporary file in the user's home.
|
||||
"""
|
||||
path = Path.home() / "temp"
|
||||
path.mkdir(exist_ok=True)
|
||||
|
||||
with tempfile.NamedTemporaryFile(dir=path, delete=False) as file:
|
||||
file.write(response.content)
|
||||
webbrowser.open(file.name)
|
||||
|
||||
|
||||
def render_template(string, context=None):
|
||||
context = context or {}
|
||||
context = Context(context)
|
||||
return Template(string).render(context)
|
||||
|
||||
|
||||
def remove_whitespaces(s) -> str:
|
||||
return s.replace("\n", "").strip()
|
@ -11,16 +11,20 @@
|
||||
{% translate "Fleet Operation Timers" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block header_nav_collapse_right %}
|
||||
{% if perms.auth.optimer_management %}
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-success" href="{% url 'optimer:add' %}">
|
||||
{% translate "Create Operation" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock header_nav_collapse_right %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<div class="text-end">
|
||||
{% if perms.auth.optimer_management %}
|
||||
<a href="{% url 'optimer:add' %}" class="btn btn-success">{% translate "Create Operation" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-3">
|
||||
<div class="badge bg-info text-start">
|
||||
<div class="badge bg-primary text-start">
|
||||
<b>{% translate "Current Eve Time:" %}</b>
|
||||
<span id="current-time"></span>
|
||||
</div>
|
||||
|
@ -23,7 +23,7 @@
|
||||
</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tab_permissions_audit" style="width: 100%;">
|
||||
<table class="table table-striped w-100" id="tab_permissions_audit">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% translate "Group" %}</th>
|
||||
@ -55,7 +55,7 @@
|
||||
{% endblock content %}
|
||||
{% block extra_javascript %}
|
||||
{% include "bundles/datatables-js-bs5.html" %}
|
||||
{# {% include "bundles/filterdropdown-js.html" %}#}
|
||||
{% include "bundles/filterdropdown-js.html" %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
@ -75,7 +75,8 @@
|
||||
idx: 0,
|
||||
title: 'Source'
|
||||
}],
|
||||
bootstrap: true
|
||||
bootstrap: true,
|
||||
bootstrap_version: 5
|
||||
},
|
||||
"stateSave": true,
|
||||
"stateDuration": 0,
|
||||
|
@ -23,7 +23,7 @@
|
||||
</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tab_permissions_overview" style="width: 100%;">
|
||||
<table class="table table-striped w-100" id="tab_permissions_overview">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% translate "App" %}</th>
|
||||
@ -60,7 +60,7 @@
|
||||
|
||||
{% block extra_javascript %}
|
||||
{% include "bundles/datatables-js-bs5.html" %}
|
||||
{# {% include "bundles/filterdropdown-js.html" %}#}
|
||||
{% include "bundles/filterdropdown-js.html" %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
@ -86,6 +86,7 @@
|
||||
}
|
||||
],
|
||||
bootstrap: true,
|
||||
bootstrap_version: 5
|
||||
},
|
||||
"stateSave": true,
|
||||
"stateDuration": 0,
|
||||
|
@ -8,9 +8,10 @@ If you wish to make changes, overload the setting in your project's settings fil
|
||||
|
||||
import os
|
||||
|
||||
from django.contrib import messages
|
||||
from celery.schedules import crontab
|
||||
|
||||
from django.contrib import messages
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'allianceauth', # needs to be on top of this list to support favicons in Django admin (see https://gitlab.com/allianceauth/allianceauth/-/issues/1301)
|
||||
'django.contrib.admin',
|
||||
@ -73,7 +74,6 @@ PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
BASE_DIR = os.path.dirname(PROJECT_DIR)
|
||||
|
||||
MIDDLEWARE = [
|
||||
'allianceauth.menu.middleware.MenuSyncMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'allianceauth.authentication.middleware.UserSettingsMiddleware',
|
||||
@ -179,7 +179,7 @@ MESSAGE_TAGS = {
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": "redis://127.0.0.1:6379/1" # change the 1 here to change the database used
|
||||
"LOCATION": "redis://127.0.0.1:6379/1" # change the 1 here for the DB used
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,6 +212,8 @@ LOGOUT_REDIRECT_URL = 'authentication:dashboard' # destination after logging ou
|
||||
# scopes required on new tokens when logging in. Cannot be blank.
|
||||
LOGIN_TOKEN_SCOPES = ['publicData']
|
||||
|
||||
EMAIL_TIMEOUT = 15
|
||||
|
||||
# number of days email verification links are valid for
|
||||
ACCOUNT_ACTIVATION_DAYS = 1
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
{% if generated != "" %}
|
||||
<div class="text-right mb-3">
|
||||
<div class="text-end mb-3">
|
||||
<textarea class="form-control" rows="10" cols="60">{{ generated }}</textarea>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="card text-center m-3" style="min-width: 18rem; min-height: 18rem;">
|
||||
<div class="card text-center m-2" style="min-width: 18rem; min-height: 18rem;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{% block title %}{% endblock title %}</h5>
|
||||
|
||||
|
@ -11,25 +11,33 @@
|
||||
{% translate "Ship Replacement Program" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block header_nav_collapse_left %}
|
||||
<a class="nav-link" href="{% url 'srp:management' %}">
|
||||
{% translate "View Fleets" %}
|
||||
</a>
|
||||
{% endblock header_nav_collapse_left %}
|
||||
|
||||
{% block header_nav_collapse_right %}
|
||||
{% if perms.auth.srp_management %}
|
||||
<li class="nav-item">
|
||||
{% if fleet_status == "Completed" %}
|
||||
<a class="btn btn-warning" href="{% url 'srp:mark_uncompleted' fleet_id %}">
|
||||
{% translate "Mark Incomplete" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="btn btn-success" href="{% url 'srp:mark_completed' fleet_id %}">
|
||||
{% translate "Mark Completed" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock header_nav_collapse_right %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
{% translate "SRP Fleet Data" as page_header %}
|
||||
{% include "framework/header/page-header.html" with title=page_header %}
|
||||
|
||||
<div class="text-end mb-3">
|
||||
{% if perms.auth.srp_management %}
|
||||
{% if fleet_status == "Completed" %}
|
||||
<a href="{% url 'srp:mark_uncompleted' fleet_id %}" class="btn btn-warning">
|
||||
{% translate "Mark Incomplete" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url 'srp:mark_completed' fleet_id %}" class="btn btn-success">
|
||||
{% translate "Mark Completed" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if srpfleetrequests %}
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
|
@ -11,22 +11,26 @@
|
||||
{% translate "Ship Replacement Program" %}
|
||||
{% endblock header_nav_brand %}
|
||||
|
||||
{% block header_nav_collapse_left %}
|
||||
{% if perms.auth.srp_management %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'srp:all' %}">
|
||||
{% translate "View All" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock header_nav_collapse_left %}
|
||||
{% block header_nav_collapse_right %}
|
||||
{% if perms.srp.add_srpfleetmain or perms.auth.srp_management %}
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-success" href="{% url 'srp:add' %}">
|
||||
{% translate "Add SRP Fleet" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock header_nav_collapse_right %}
|
||||
{% block content %}
|
||||
<div>
|
||||
<div class="text-end mb-3">
|
||||
{% if perms.auth.srp_management %}
|
||||
<a href="{% url 'srp:all' %}" class="btn btn-primary">
|
||||
{% translate "View All" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.srp.add_srpfleetmain or perms.auth.srp_management %}
|
||||
<a href="{% url 'srp:add' %}" class="btn btn-success">
|
||||
{% translate "Add SRP Fleet" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
<div class="text-end">
|
||||
<b>{% translate "Total ISK Cost:" %} {{ totalcost | intcomma }}</b>
|
||||
@ -52,9 +56,7 @@
|
||||
{% for srpfleet in srpfleets %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="badge bg-info">
|
||||
{{ srpfleet.fleet_name }}
|
||||
</div>
|
||||
{{ srpfleet.fleet_name }}
|
||||
</td>
|
||||
<td>{{ srpfleet.fleet_time | date:"Y-m-d H:i" }}</td>
|
||||
<td>{{ srpfleet.fleet_doctrine }}</td>
|
||||
@ -93,7 +95,7 @@
|
||||
<td>
|
||||
<div class="badge bg-warning">{{ srpfleet.pending_requests }}</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<td class="text-end text-nowrap">
|
||||
<a href="{% url 'srp:fleet' srpfleet.id %}" class="btn btn-primary btn-sm m-1" title="View">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</a>
|
||||
|
@ -1,239 +1,296 @@
|
||||
/*
|
||||
/**
|
||||
* filterDropDown.js
|
||||
*
|
||||
* Copyright (C) 2017-18 Erik Kalkoken
|
||||
* Copyright (C) 2017-24 Erik Kalkoken
|
||||
*
|
||||
* Extension for the jQuery plug-in DataTables (developed and tested with v1.10.15)
|
||||
* Extension for the jQuery plug-in DataTables (developed and tested with v1.13.7)
|
||||
*
|
||||
* Version 0.4.0
|
||||
*
|
||||
**/
|
||||
* Version 0.5.0
|
||||
**/
|
||||
(($) => {
|
||||
"use strict";
|
||||
|
||||
(function ($) {
|
||||
|
||||
// parse initialization array and returns filterDef array to faster and easy use
|
||||
// also sets defaults for properties that are not set
|
||||
function parseInitArray(initArray) {
|
||||
// initialization and setting defaults
|
||||
let filterDef = {
|
||||
"columns": [],
|
||||
"columnsIdxList": [],
|
||||
"bootstrap": false,
|
||||
"autoSize": true,
|
||||
"ajax": null,
|
||||
"label": "Filter "
|
||||
/**
|
||||
* Parse initialization array and returns filterDef array to faster and easy use,
|
||||
* also sets defaults for properties that are not set
|
||||
*
|
||||
* @param initArray
|
||||
* @returns {{autoSize: boolean, bootstrap_version: number, columnsIdxList: *[], columns: *[], bootstrap: boolean, label: string, ajax: null}}
|
||||
*/
|
||||
const parseInitArray = (initArray) => {
|
||||
/**
|
||||
* Default filter definition
|
||||
*
|
||||
* @type {{autoSize: boolean, bootstrap_version: number, columnsIdxList: *[], columns: *[], bootstrap: boolean, label: string, ajax: null}}
|
||||
*/
|
||||
const filterDef = {
|
||||
columns: [],
|
||||
columnsIdxList: [],
|
||||
bootstrap: false,
|
||||
bootstrap_version: 3,
|
||||
autoSize: true,
|
||||
ajax: null,
|
||||
label: "Filter ",
|
||||
};
|
||||
|
||||
// set filter properties if they have been defined
|
||||
// otherwise the defaults will be used
|
||||
if (("bootstrap" in initArray) && (typeof initArray.bootstrap === 'boolean')) {
|
||||
// Set filter properties if they have been defined otherwise the defaults will be used
|
||||
if (
|
||||
"bootstrap" in initArray &&
|
||||
typeof initArray.bootstrap === "boolean"
|
||||
) {
|
||||
filterDef.bootstrap = initArray.bootstrap;
|
||||
}
|
||||
|
||||
if (("autoSize" in initArray) && (typeof initArray.autoSize === 'boolean')) {
|
||||
if (
|
||||
"bootstrap_version" in initArray &&
|
||||
typeof initArray.bootstrap_version === "number"
|
||||
) {
|
||||
filterDef.bootstrap_version = initArray.bootstrap_version;
|
||||
}
|
||||
|
||||
if (
|
||||
"autoSize" in initArray &&
|
||||
typeof initArray.autoSize === "boolean"
|
||||
) {
|
||||
filterDef.autoSize = initArray.autoSize;
|
||||
}
|
||||
|
||||
if (("ajax" in initArray) && (typeof initArray.ajax === 'string')) {
|
||||
if ("ajax" in initArray && typeof initArray.ajax === "string") {
|
||||
filterDef.ajax = initArray.ajax;
|
||||
}
|
||||
|
||||
if (("label" in initArray) && (typeof initArray.label === 'string')) {
|
||||
if ("label" in initArray && typeof initArray.label === "string") {
|
||||
filterDef.label = initArray.label;
|
||||
}
|
||||
|
||||
// add definition for each column
|
||||
// Add definition for each column
|
||||
if ("columns" in initArray) {
|
||||
initArray.columns.forEach(function (initColumn) {
|
||||
if (("idx" in initColumn) && (typeof initColumn.idx === 'number')) {
|
||||
// initialize column
|
||||
let idx = initColumn.idx;
|
||||
filterDef['columns'][idx] = {
|
||||
"title": null,
|
||||
"maxWidth": null,
|
||||
"autoSize": true
|
||||
initArray.columns.forEach((initColumn) => {
|
||||
if ("idx" in initColumn && typeof initColumn.idx === "number") {
|
||||
// Initialize column
|
||||
const idx = initColumn.idx;
|
||||
|
||||
filterDef.columns[idx] = {
|
||||
title: null,
|
||||
maxWidth: null,
|
||||
autoSize: true,
|
||||
};
|
||||
|
||||
// add to list of indices in same order they appear in the init array
|
||||
filterDef['columnsIdxList'].push(idx);
|
||||
// Add to a list of indices in the same order they appear in the init array
|
||||
filterDef.columnsIdxList.push(idx);
|
||||
|
||||
// set column properties if they have been defined
|
||||
// otherwise the defaults will be used
|
||||
if (('title' in initColumn)
|
||||
&& (typeof initColumn.title === 'string')
|
||||
// Set column properties if they have been defined otherwise the defaults will be used
|
||||
if (
|
||||
"title" in initColumn &&
|
||||
typeof initColumn.title === "string"
|
||||
) {
|
||||
filterDef['columns'][idx].title = initColumn.title;
|
||||
filterDef.columns[idx].title = initColumn.title;
|
||||
}
|
||||
|
||||
if (('maxWidth' in initColumn)
|
||||
&& (typeof initColumn.maxWidth === 'string')
|
||||
if (
|
||||
"maxWidth" in initColumn &&
|
||||
typeof initColumn.maxWidth === "string"
|
||||
) {
|
||||
filterDef['columns'][idx].maxWidth = initColumn.maxWidth;
|
||||
filterDef.columns[idx].maxWidth = initColumn.maxWidth;
|
||||
}
|
||||
|
||||
if (('autoSize' in initColumn)
|
||||
&& (typeof initColumn.autoSize === 'boolean')
|
||||
if (
|
||||
"autoSize" in initColumn &&
|
||||
typeof initColumn.autoSize === "boolean"
|
||||
) {
|
||||
filterDef['columns'][idx].autoSize = initColumn.autoSize;
|
||||
filterDef.columns[idx].autoSize = initColumn.autoSize;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return filterDef;
|
||||
}
|
||||
};
|
||||
|
||||
// Add option d to given select object
|
||||
function addOption(select, d) {
|
||||
if (d != "") {
|
||||
select.append('<option value="' + d + '">' + d + '</option>');
|
||||
/**
|
||||
* Add option d to the given select object
|
||||
*
|
||||
* @param select
|
||||
* @param d
|
||||
*/
|
||||
const addOption = (select, d) => {
|
||||
if (d !== "") {
|
||||
select.append(`<option value="${d}">${d}</option>`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// initalizing select for current column and applying event to react to changes
|
||||
function initSelectForColumn(id, column) {
|
||||
let select = $("#" + id + "_filterSelect" + column.index());
|
||||
select.on('change', function () {
|
||||
let val = $.fn.dataTable.util.escapeRegex($(this).val());
|
||||
column
|
||||
.search(val ? '^' + val + '$' : '', true, false)
|
||||
.draw();
|
||||
/**
|
||||
* Initialize the select element for given column and apply event to react to changes
|
||||
*
|
||||
* @param id
|
||||
* @param column
|
||||
* @returns {*|jQuery|HTMLElement}
|
||||
*/
|
||||
const initSelectForColumn = (id, column) => {
|
||||
const select = $(`#${id}_filterSelect${column.index()}`);
|
||||
|
||||
$(select).change(() => {
|
||||
const val = $.fn.dataTable.util.escapeRegex($(select).val());
|
||||
|
||||
column.search(val ? `^${val}$` : "", true, false).draw();
|
||||
});
|
||||
return select
|
||||
}
|
||||
|
||||
// Add filterDropDown container div, draw select elements with default options
|
||||
// use preInit so that elements are created and correctly shown before data is loaded
|
||||
$(document).on('preInit.dt', function (e, settings) {
|
||||
if (e.namespace !== 'dt') return;
|
||||
return select;
|
||||
};
|
||||
|
||||
// get api object for current dt table
|
||||
var api = new $.fn.dataTable.Api(settings);
|
||||
// Add filterDropDown container div, draw select elements with default options.
|
||||
// Use preInit so that elements are created and correctly shown before data is loaded
|
||||
$(document).on("preInit.dt", (e, settings) => {
|
||||
if (e.namespace !== "dt") {
|
||||
return;
|
||||
}
|
||||
|
||||
// get id of current table
|
||||
var id = api.table().node().id;
|
||||
// Get the api object for the current dt table
|
||||
const api = new $.fn.dataTable.Api(settings);
|
||||
|
||||
// get initialization object for current table to retrieve custom settings
|
||||
var initObj = api.init();
|
||||
// Get the id of the current table
|
||||
const id = api.table().node().id;
|
||||
|
||||
// only proceed if filter has been defined in current table, otherwise don't do anything.
|
||||
if (!("filterDropDown" in initObj)) return;
|
||||
// Get the initialization object for the current table to retrieve custom settings
|
||||
const initObj = api.init();
|
||||
|
||||
// get current filter definition from init array
|
||||
var filterDef = parseInitArray(initObj.filterDropDown);
|
||||
// Only proceed if the filter has been defined in the current table,
|
||||
// otherwise don't do anything.
|
||||
if (!("filterDropDown" in initObj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current filter definition from the init array
|
||||
const filterDef = parseInitArray(initObj.filterDropDown);
|
||||
|
||||
// only proceed if there are any columns defined
|
||||
if (filterDef.columns.length == 0) return;
|
||||
if (filterDef.columns.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get container div for current data table to add new elements to
|
||||
var container = api.table().container();
|
||||
// Get container div for the current data table to add new elements to
|
||||
const container = api.table().container();
|
||||
|
||||
// Add filter elements to DOM
|
||||
const filterWrapperId = `${id}_filterWrapper`;
|
||||
|
||||
// Set CSS classes for the filter wrapper div depending on bootstrap setting
|
||||
let divCssClass = `${filterWrapperId} ${
|
||||
filterDef.bootstrap ? "form-inline" : ""
|
||||
}`;
|
||||
if (filterDef.bootstrap && filterDef.bootstrap_version === 5) {
|
||||
divCssClass = `${filterWrapperId} input-group my-3`;
|
||||
}
|
||||
|
||||
// add filter elements to DOM
|
||||
var filterWrapperId = id + "_filterWrapper";
|
||||
var divCssClass = filterWrapperId + " " + (
|
||||
(filterDef.bootstrap)
|
||||
? "form-inline"
|
||||
: ""
|
||||
);
|
||||
$(container).prepend(
|
||||
'<div id="'
|
||||
+ filterWrapperId
|
||||
+ '" class="'
|
||||
+ divCssClass + '">'
|
||||
+ filterDef.label
|
||||
+ '</div>'
|
||||
`<div id="${filterWrapperId}" class="${divCssClass}"><span class="pt-2">${filterDef.label}</span></div>`
|
||||
);
|
||||
|
||||
api.columns(filterDef.columnsIdxList).every(function () {
|
||||
let idx = this.index();
|
||||
const idx = this.index();
|
||||
|
||||
// set title of current column
|
||||
let colName = (filterDef.columns[idx].title !== null)
|
||||
? filterDef.columns[idx].title
|
||||
: $(this.header()).html();
|
||||
let colName =
|
||||
filterDef.columns[idx].title !== null
|
||||
? filterDef.columns[idx].title
|
||||
: $(this.header()).html();
|
||||
|
||||
if (colName == "") colName = 'column ' + (idx + 1);
|
||||
|
||||
// adding select element for current column to container
|
||||
let selectId = id + "_filterSelect" + idx;
|
||||
$('#' + filterWrapperId).append(
|
||||
'<select id="'
|
||||
+ selectId
|
||||
+ '" class="form-control '
|
||||
+ id
|
||||
+ '_filterSelect"></select>'
|
||||
);
|
||||
|
||||
// initalizing select for current column and applying event to react to changes
|
||||
let select = $("#" + selectId).empty()
|
||||
.append('<option value="">(' + colName + ')</option>');
|
||||
|
||||
// set max width of select elements to current width (which is defined by the size of the title)
|
||||
// turn off on for very small screens for responsive design or if autoSize has been set to false
|
||||
if (filterDef.autoSize && filterDef.columns[idx].autoSize && (screen.width > 768)) {
|
||||
select.css('max-width', select.outerWidth());
|
||||
if (colName === "") {
|
||||
colName = `column ${idx + 1}`;
|
||||
}
|
||||
|
||||
// apply optional css style if defined in init array
|
||||
// will override automatic max width setting
|
||||
// Adding the select element for current column to container
|
||||
const selectId = `${id}_filterSelect${idx}`;
|
||||
|
||||
// Set CSS classes for the select element depending on bootstrap setting
|
||||
let selectMarkup = `<select id="${selectId}" class="form-control ${id}_filterSelect"></select>`;
|
||||
if (filterDef.bootstrap && filterDef.bootstrap_version === 5) {
|
||||
selectMarkup = `<select id="${selectId}" class="form-select w-auto ms-2 ${id}_filterSelect"></select>`;
|
||||
}
|
||||
|
||||
$("#" + filterWrapperId).append(selectMarkup);
|
||||
|
||||
// Initializing select for current column and applying event to react to changes
|
||||
const select = $("#" + selectId)
|
||||
.empty()
|
||||
.append(`<option value="">(${colName})</option>`);
|
||||
|
||||
// Set max width of select elements to current width (which is defined by the size of the title)
|
||||
// Turn off on for very small screens for responsive design, or if autoSize has been set to false
|
||||
if (
|
||||
filterDef.autoSize &&
|
||||
filterDef.columns[idx].autoSize &&
|
||||
screen.width > 768
|
||||
) {
|
||||
select.css("max-width", select.outerWidth());
|
||||
}
|
||||
|
||||
// Apply optional css style if defined in the init array will override automatic max width setting
|
||||
if (filterDef.columns[idx].maxWidth !== null) {
|
||||
select.css('max-width', filterDef.columns[idx].maxWidth);
|
||||
select.css("max-width", filterDef.columns[idx].maxWidth);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// filter table and add available options to dropDowns
|
||||
$(document).on('init.dt', function (e, settings) {
|
||||
if (e.namespace !== 'dt') return;
|
||||
// Filter table and add available options to dropDowns
|
||||
$(document).on("init.dt", (e, settings) => {
|
||||
if (e.namespace !== "dt") {
|
||||
return;
|
||||
}
|
||||
|
||||
// get api object for current dt table
|
||||
var api = new $.fn.dataTable.Api(settings);
|
||||
// Get api object for current dt table
|
||||
const api = new $.fn.dataTable.Api(settings);
|
||||
|
||||
// get id of current table
|
||||
var id = api.table().node().id;
|
||||
// Get id of current table
|
||||
const id = api.table().node().id;
|
||||
|
||||
// get initialization object for current table to retrieve custom settings
|
||||
var initObj = api.init();
|
||||
// Get the initialization object for current table to retrieve custom settings
|
||||
const initObj = api.init();
|
||||
|
||||
// only proceed if filter has been defined in current table, otherwise don't do anything.
|
||||
if (!("filterDropDown" in initObj)) return;
|
||||
// Only proceed if a filter has been defined in the current table, otherwise don't do anything.
|
||||
if (!("filterDropDown" in initObj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get current filter definition
|
||||
var filterDef = parseInitArray(initObj.filterDropDown);
|
||||
// Get current filter definition
|
||||
const filterDef = parseInitArray(initObj.filterDropDown);
|
||||
|
||||
if (filterDef.ajax == null) {
|
||||
api.columns(filterDef.columnsIdxList).every(function () {
|
||||
let column = this
|
||||
let select = initSelectForColumn(id, column);
|
||||
column.data().unique().sort().each(function (d) {
|
||||
addOption(select, d)
|
||||
});
|
||||
const column = this;
|
||||
const select = initSelectForColumn(id, column);
|
||||
|
||||
column
|
||||
.data()
|
||||
.unique()
|
||||
.sort()
|
||||
.each((d) => {
|
||||
addOption(select, d);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// fetch column options from server for server side processing
|
||||
let columnsQuery = (
|
||||
"columns="
|
||||
+ encodeURIComponent(
|
||||
api.columns(filterDef.columnsIdxList).dataSrc().join()
|
||||
)
|
||||
)
|
||||
$.getJSON(filterDef.ajax + "?" + columnsQuery, function (columnsOptions) {
|
||||
// Fetch column options from server for server side processing
|
||||
const columnsQuery = `columns=${encodeURIComponent(
|
||||
api.columns(filterDef.columnsIdxList).dataSrc().join()
|
||||
)}`;
|
||||
|
||||
$.getJSON(`${filterDef.ajax}?${columnsQuery}`, (columnsOptions) => {
|
||||
api.columns(filterDef.columnsIdxList).every(function () {
|
||||
let column = this;
|
||||
let select = initSelectForColumn(id, column);
|
||||
let columnName = column.dataSrc()
|
||||
const column = this;
|
||||
const select = initSelectForColumn(id, column);
|
||||
const columnName = column.dataSrc();
|
||||
|
||||
if (columnName in columnsOptions) {
|
||||
columnsOptions[columnName].forEach(function (d) {
|
||||
addOption(select, d)
|
||||
columnsOptions[columnName].forEach((d) => {
|
||||
addOption(select, d);
|
||||
});
|
||||
} else {
|
||||
console.warn(
|
||||
"Missing column '" + columnName + "' in ajax response."
|
||||
)
|
||||
`Missing column '${columnName}' in ajax response.`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery));
|
||||
})(jQuery);
|
||||
|
@ -1 +1 @@
|
||||
!function(t){function n(t){let n={columns:[],columnsIdxList:[],bootstrap:!1,autoSize:!0,ajax:null,label:"Filter "};return"bootstrap"in t&&"boolean"==typeof t.bootstrap&&(n.bootstrap=t.bootstrap),"autoSize"in t&&"boolean"==typeof t.autoSize&&(n.autoSize=t.autoSize),"ajax"in t&&"string"==typeof t.ajax&&(n.ajax=t.ajax),"label"in t&&"string"==typeof t.label&&(n.label=t.label),"columns"in t&&t.columns.forEach(function(t){if("idx"in t&&"number"==typeof t.idx){let e=t.idx;n.columns[e]={title:null,maxWidth:null,autoSize:!0},n.columnsIdxList.push(e),"title"in t&&"string"==typeof t.title&&(n.columns[e].title=t.title),"maxWidth"in t&&"string"==typeof t.maxWidth&&(n.columns[e].maxWidth=t.maxWidth),"autoSize"in t&&"boolean"==typeof t.autoSize&&(n.columns[e].autoSize=t.autoSize)}}),n}function e(t,n){""!=n&&t.append('<option value="'+n+'">'+n+"</option>")}function i(n,e){let i=t("#"+n+"_filterSelect"+e.index());return i.on("change",function(){let n=t.fn.dataTable.util.escapeRegex(t(this).val());e.search(n?"^"+n+"$":"",!0,!1).draw()}),i}t(document).on("preInit.dt",function(e,i){if("dt"===e.namespace){var o=new t.fn.dataTable.Api(i),l=o.table().node().id,a=o.init();if("filterDropDown"in a){var u=n(a.filterDropDown);if(0!=u.columns.length){var s=o.table().container(),c=l+"_filterWrapper",r=c+" "+(u.bootstrap?"form-inline":"");t(s).prepend('<div id="'+c+'" class="'+r+'">'+u.label+"</div>"),o.columns(u.columnsIdxList).every(function(){let n=this.index(),e=null!==u.columns[n].title?u.columns[n].title:t(this.header()).html();""==e&&(e="column "+(n+1));let i=l+"_filterSelect"+n;t("#"+c).append('<select id="'+i+'" class="form-control '+l+'_filterSelect"></select>');let o=t("#"+i).empty().append('<option value="">('+e+")</option>");u.autoSize&&u.columns[n].autoSize&&screen.width>768&&o.css("max-width",o.outerWidth()),null!==u.columns[n].maxWidth&&o.css("max-width",u.columns[n].maxWidth)})}}}}),t(document).on("init.dt",function(o,l){if("dt"===o.namespace){var a=new t.fn.dataTable.Api(l),u=a.table().node().id,s=a.init();if("filterDropDown"in s){var c=n(s.filterDropDown);if(null==c.ajax)a.columns(c.columnsIdxList).every(function(){let t=i(u,this);this.data().unique().sort().each(function(n){e(t,n)})});else{let n="columns="+encodeURIComponent(a.columns(c.columnsIdxList).dataSrc().join());t.getJSON(c.ajax+"?"+n,function(t){a.columns(c.columnsIdxList).every(function(){let n=i(u,this),o=this.dataSrc();o in t?t[o].forEach(function(t){e(n,t)}):console.warn("Missing column '"+o+"' in ajax response.")})})}}}})}(jQuery);
|
||||
($=>{"use strict";const parseInitArray=initArray=>{const filterDef={columns:[],columnsIdxList:[],bootstrap:false,bootstrap_version:3,autoSize:true,ajax:null,label:"Filter "};if("bootstrap"in initArray&&typeof initArray.bootstrap==="boolean"){filterDef.bootstrap=initArray.bootstrap}if("bootstrap_version"in initArray&&typeof initArray.bootstrap_version==="number"){filterDef.bootstrap_version=initArray.bootstrap_version}if("autoSize"in initArray&&typeof initArray.autoSize==="boolean"){filterDef.autoSize=initArray.autoSize}if("ajax"in initArray&&typeof initArray.ajax==="string"){filterDef.ajax=initArray.ajax}if("label"in initArray&&typeof initArray.label==="string"){filterDef.label=initArray.label}if("columns"in initArray){initArray.columns.forEach(initColumn=>{if("idx"in initColumn&&typeof initColumn.idx==="number"){const idx=initColumn.idx;filterDef.columns[idx]={title:null,maxWidth:null,autoSize:true};filterDef.columnsIdxList.push(idx);if("title"in initColumn&&typeof initColumn.title==="string"){filterDef.columns[idx].title=initColumn.title}if("maxWidth"in initColumn&&typeof initColumn.maxWidth==="string"){filterDef.columns[idx].maxWidth=initColumn.maxWidth}if("autoSize"in initColumn&&typeof initColumn.autoSize==="boolean"){filterDef.columns[idx].autoSize=initColumn.autoSize}}})}return filterDef};const addOption=(select,d)=>{if(d!==""){select.append(`<option value="${d}">${d}</option>`)}};const initSelectForColumn=(id,column)=>{const select=$(`#${id}_filterSelect${column.index()}`);$(select).change(()=>{const val=$.fn.dataTable.util.escapeRegex($(select).val());column.search(val?`^${val}$`:"",true,false).draw()});return select};$(document).on("preInit.dt",(e,settings)=>{if(e.namespace!=="dt"){return}const api=new $.fn.dataTable.Api(settings);const id=api.table().node().id;const initObj=api.init();if(!("filterDropDown"in initObj)){return}const filterDef=parseInitArray(initObj.filterDropDown);if(filterDef.columns.length===0){return}const container=api.table().container();const filterWrapperId=`${id}_filterWrapper`;let divCssClass=`${filterWrapperId} ${filterDef.bootstrap?"form-inline":""}`;if(filterDef.bootstrap&&filterDef.bootstrap_version===5){divCssClass=`${filterWrapperId} input-group my-3`}$(container).prepend(`<div id="${filterWrapperId}" class="${divCssClass}"><span class="pt-2">${filterDef.label}</span></div>`);api.columns(filterDef.columnsIdxList).every(function(){const idx=this.index();let colName=filterDef.columns[idx].title!==null?filterDef.columns[idx].title:$(this.header()).html();if(colName===""){colName=`column ${idx+1}`}const selectId=`${id}_filterSelect${idx}`;let selectMarkup=`<select id="${selectId}" class="form-control ${id}_filterSelect"></select>`;if(filterDef.bootstrap&&filterDef.bootstrap_version===5){selectMarkup=`<select id="${selectId}" class="form-select w-auto ms-2 ${id}_filterSelect"></select>`}$("#"+filterWrapperId).append(selectMarkup);const select=$("#"+selectId).empty().append(`<option value="">(${colName})</option>`);if(filterDef.autoSize&&filterDef.columns[idx].autoSize&&screen.width>768){select.css("max-width",select.outerWidth())}if(filterDef.columns[idx].maxWidth!==null){select.css("max-width",filterDef.columns[idx].maxWidth)}})});$(document).on("init.dt",(e,settings)=>{if(e.namespace!=="dt"){return}const api=new $.fn.dataTable.Api(settings);const id=api.table().node().id;const initObj=api.init();if(!("filterDropDown"in initObj)){return}const filterDef=parseInitArray(initObj.filterDropDown);if(filterDef.ajax==null){api.columns(filterDef.columnsIdxList).every(function(){const column=this;const select=initSelectForColumn(id,column);column.data().unique().sort().each(d=>{addOption(select,d)})})}else{const columnsQuery=`columns=${encodeURIComponent(api.columns(filterDef.columnsIdxList).dataSrc().join())}`;$.getJSON(`${filterDef.ajax}?${columnsQuery}`,columnsOptions=>{api.columns(filterDef.columnsIdxList).every(function(){const column=this;const select=initSelectForColumn(id,column);const columnName=column.dataSrc();if(columnName in columnsOptions){columnsOptions[columnName].forEach(d=>{addOption(select,d)})}else{console.warn(`Missing column '${columnName}' in ajax response.`)}})})}})})(jQuery);
|
||||
|
@ -0,0 +1,37 @@
|
||||
{% load i18n %}
|
||||
<div id="esi-alert" class="col-12 align-self-stretch py-2 collapse">
|
||||
<div class="alert alert-warning">
|
||||
<p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p>
|
||||
<hr>
|
||||
<pre id="esi-data" class="text-center text-wrap"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const elemCard = document.getElementById("esi-alert");
|
||||
const elemMessage = document.getElementById("esi-data");
|
||||
const elemCode = document.getElementById("esi-code");
|
||||
|
||||
fetch('{% url "authentication:esi_check" %}')
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw new Error("Something went wrong");
|
||||
})
|
||||
.then((responseJson) => {
|
||||
console.log("ESI Check: ", JSON.stringify(responseJson, null, 2));
|
||||
|
||||
const status = responseJson.status;
|
||||
if (status != 200) {
|
||||
elemCode.textContent = status
|
||||
elemMessage.textContent = responseJson.data.error;
|
||||
new bootstrap.Collapse(elemCard, {
|
||||
toggle: true
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
</script>
|
@ -2,7 +2,7 @@
|
||||
{% load humanize %}
|
||||
|
||||
{% if notifications %}
|
||||
<div class="col-12 align-self-stretch pb-2">
|
||||
<div id="aa-dashboard-panel-admin-notifications" class="col-12 align-self-stretch pb-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
@ -51,10 +51,10 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="col-12 align-self-stretch py-2">
|
||||
<div class="col-12 align-self-stretch pb-2">
|
||||
<div class="card">
|
||||
<div class="card-body d-flex flex-row flex-wrap">
|
||||
<div class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
|
||||
<div id="aa-dashboard-panel-software-version" class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
|
||||
<h4 class="ms-auto me-auto text-center">
|
||||
{% translate "Software Version" %}
|
||||
</h4>
|
||||
@ -97,7 +97,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
|
||||
<div id="aa-dashboard-panel-task-queue" class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
|
||||
<h4 class="ms-auto me-auto text-center">
|
||||
{% translate "Task Queue" %}
|
||||
</h4>
|
||||
|
@ -34,12 +34,6 @@
|
||||
padding-top: {% header_padding_size %} !important;
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
.auth-logo {
|
||||
background-position: bottom;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("{% static 'allianceauth/images/auth-logo.png' %}") ;
|
||||
}
|
||||
</style>
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
</head>
|
||||
@ -59,20 +53,18 @@
|
||||
{% block header_nav_brand %}{{ SITE_NAME }}{% endblock %}
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarexpand">
|
||||
<div class="m-2"></div>
|
||||
|
||||
<ul id="nav-left" class="navbar-nav nav me-auto">
|
||||
<div class="collapse navbar-collapse ms-2 px-2" id="navbarexpand">
|
||||
<ul id="nav-left" class="nav navbar-nav me-auto">
|
||||
{% block header_nav_collapse_left %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
|
||||
<ul id="nav-right" class="navbar-nav">
|
||||
<ul id="nav-right" class="nav navbar-nav">
|
||||
{% block header_nav_collapse_right %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
|
||||
<ul id="nav-right-character-control" class="navbar-nav">
|
||||
<ul id="nav-right-character-control" class="nav navbar-nav">
|
||||
{% block header_nav_user_character_control %} <!-- Default to add char and swap main -->
|
||||
{% include 'allianceauth/top-menu-rh-default.html' %}
|
||||
{% endblock %}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
{% load navactive %}
|
||||
{% load menu_menu_items %}
|
||||
{% load menu_items %}
|
||||
|
||||
<div class="col-sm-2 auth-side-navbar" role="navigation">
|
||||
<div class="collapse navbar-collapse auth-menus-collapse auth-side-navbar-collapse">
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
|
||||
<li class="nav-item active">
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'authentication:add_character' %}" class="nav-link" title="{% translate 'Add Character' %}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
<span class="d-lg-none d-md-inline m-2">{% translate "Add Character" %}</span>
|
||||
|
3
allianceauth/templates/bundles/image-auth-logo.html
Normal file
3
allianceauth/templates/bundles/image-auth-logo.html
Normal file
@ -0,0 +1,3 @@
|
||||
{% load static %}
|
||||
|
||||
<img src="{% static 'allianceauth/images/auth-logo.svg' %}" width="{{ logo_width|default:"128px" }}" height="{% if logo_height %}{{ logo_ }}{% else %}{{ logo_width|default:"128px" }}{% endif %}" alt="{{ SITE_NAME }}">
|
@ -2,6 +2,20 @@ from allianceauth import hooks
|
||||
from allianceauth.theme.hooks import ThemeHook
|
||||
|
||||
|
||||
CSS_STATICS = [{
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css",
|
||||
"integrity": "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
|
||||
}]
|
||||
|
||||
JS_STATICS = [{
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
||||
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
||||
}, {
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
|
||||
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
|
||||
}]
|
||||
|
||||
|
||||
class BootstrapThemeHook(ThemeHook):
|
||||
"""
|
||||
Bootstrap in all its glory!
|
||||
@ -13,21 +27,35 @@ class BootstrapThemeHook(ThemeHook):
|
||||
self,
|
||||
"Bootstrap",
|
||||
"Powerful, extensible, and feature-packed frontend toolkit.",
|
||||
css=[{
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css",
|
||||
"integrity": "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
|
||||
}],
|
||||
js=[{
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
||||
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
||||
}, {
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
|
||||
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
|
||||
}],
|
||||
css=CSS_STATICS,
|
||||
js=JS_STATICS,
|
||||
header_padding="3.5em"
|
||||
)
|
||||
|
||||
|
||||
class BootstrapDarkThemeHook(ThemeHook):
|
||||
"""
|
||||
Bootstrap in all its glory!, but _dark_
|
||||
https://getbootstrap.com/
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ThemeHook.__init__(
|
||||
self,
|
||||
"Bootstrap Dark",
|
||||
"Powerful, extensible, and feature-packed frontend toolkit.",
|
||||
css=CSS_STATICS,
|
||||
js=JS_STATICS,
|
||||
html_tags="data-bs-theme=dark",
|
||||
header_padding="3.5em"
|
||||
)
|
||||
|
||||
|
||||
@hooks.register('theme_hook')
|
||||
def register_bootstrap_dark_hook():
|
||||
return BootstrapDarkThemeHook()
|
||||
|
||||
|
||||
@hooks.register('theme_hook')
|
||||
def register_bootstrap_hook():
|
||||
return BootstrapThemeHook()
|
||||
|
@ -1,11 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BootstrapDarkThemeConfig(AppConfig):
|
||||
name = "allianceauth.theme.bootstrap_dark"
|
||||
label = "bootstrap_dark"
|
||||
version = "5.3.0"
|
||||
verbose_name = f"Bootstrap Dark v{version}"
|
||||
|
||||
def ready(self):
|
||||
pass
|
@ -1,34 +0,0 @@
|
||||
from allianceauth import hooks
|
||||
from allianceauth.theme.hooks import ThemeHook
|
||||
|
||||
|
||||
class BootstrapDarkThemeHook(ThemeHook):
|
||||
"""
|
||||
Bootstrap in all its glory!, but _dark_
|
||||
https://getbootstrap.com/
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ThemeHook.__init__(
|
||||
self,
|
||||
"Bootstrap Dark",
|
||||
"Powerful, extensible, and feature-packed frontend toolkit.",
|
||||
css=[{
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css",
|
||||
"integrity": "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
|
||||
}],
|
||||
js=[{
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
|
||||
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
|
||||
}, {
|
||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
|
||||
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
|
||||
}],
|
||||
html_tags="data-bs-theme=dark",
|
||||
header_padding="3.5em"
|
||||
)
|
||||
|
||||
|
||||
@hooks.register('theme_hook')
|
||||
def register_bootstrap_dark_hook():
|
||||
return BootstrapDarkThemeHook()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user