Compare commits

...

23 Commits

Author SHA1 Message Date
Ariel Rin
5250432ce3 Version Bump v2.6.3 2020-04-02 03:51:59 +00:00
Ariel Rin
53d6e973eb Merge branch 'i18n-chinese' into 'master'
Update Translations from Transifex

See merge request allianceauth/allianceauth!1190
2020-04-02 03:32:39 +00:00
Ariel Rin
c9bdd62d53 Update Translations from Transifex 2020-04-02 03:32:39 +00:00
Ariel Rin
7eb98af528 Merge branch 'issue_1225' into 'master'
Fix broken link and remove outdated migrations for services name formatter

Closes #1225

See merge request allianceauth/allianceauth!1183
2020-04-02 03:04:55 +00:00
Ariel Rin
385e3e21b3 Merge branch 'improve_groups_view' into 'master'
Add sorting to group view and add tests to group management

See merge request allianceauth/allianceauth!1172
2020-04-02 03:01:27 +00:00
Erik Kalkoken
127ec63d76 Add sorting to group view and add tests to group management 2020-04-02 03:01:27 +00:00
Ariel Rin
4988b5f260 Merge branch 'common_logger' into 'master'
Extensions Logging

See merge request allianceauth/allianceauth!1180
2020-04-02 02:59:55 +00:00
Ariel Rin
f28a50f92c Merge branch 'fix_translations_3' into 'master'
Add missing translations

See merge request allianceauth/allianceauth!1186
2020-03-26 03:06:11 +00:00
Ariel Rin
e8efe8e609 Merge branch 'i18n-chinese' into 'master'
Add Korean and Russian, Update from Transifex

See merge request allianceauth/allianceauth!1179
2020-03-26 02:50:25 +00:00
Ariel Rin
d7e7457bc5 Add Korean and Russian, Update from Transifex 2020-03-26 02:50:25 +00:00
Ariel Rin
daff927811 Merge branch 'prioritize_celery' into 'master'
Add Celery Priorities

See merge request allianceauth/allianceauth!1181
2020-03-26 02:20:02 +00:00
Aaron Kable
8861ec0a61 Add Celery Priorities 2020-03-26 02:20:02 +00:00
Ariel Rin
bd4321f61a Merge branch 'docs_update' into 'master'
Docs only: Harmonize gunicorn config, add localization feature

See merge request allianceauth/allianceauth!1184
2020-03-26 01:55:03 +00:00
Erik Kalkoken
d831482fe0 Docs only: Harmonize gunicorn config, add localization feature 2020-03-26 01:55:03 +00:00
ErikKalkoken
73f262ce4b Add missing translations 2020-03-24 20:21:35 +01:00
ErikKalkoken
f63434adc3 Fix broken link and remove outdated migrations for services name formatter 2020-03-21 14:41:45 +01:00
Ariel Rin
32e0621b0a Merge branch 'improve_install_docu' into 'master'
Docs: Add python upgrade guide, remove old AA 1.15 upgrade guide, improve install guide

See merge request allianceauth/allianceauth!1177
2020-03-15 12:21:21 +00:00
Erik Kalkoken
78e05b84e9 Docs: Add python upgrade guide, remove old AA 1.15 upgrade guide, improve install guide 2020-03-15 12:21:21 +00:00
Col Crunch
76ebd21163 Add function to services.hooks to provide a concise way for creating loggers for extensions/plugins. Revise basic documentation to use this function. 2020-03-13 15:21:15 -04:00
Col Crunch
38aaf545c6 Add some very basic docs for logging changes 2020-03-13 14:42:09 -04:00
Col Crunch
527d7ef671 Change level of extension_file handler, rename the logger from allianceauth.extensions to extensions and remove propagate from the logger. 2020-03-13 04:42:09 -04:00
Col Crunch
e54b80e061 Add a common logger (and specific log file) for extensions to utilize. 2020-03-13 00:33:35 -04:00
Col Crunch
27f95a8b2c Remove Zone.Indentifier files. 2020-03-12 23:55:34 -04:00
66 changed files with 6313 additions and 627 deletions

View File

@@ -11,32 +11,34 @@
An auth system for EVE Online to help in-game organizations manage online service access.
## Contens
## Content
- [Overview](#overview)
- [Documentation](http://allianceauth.rtfd.io)
- [Support](#support)
- [Release Notes](https://gitlab.com/allianceauth/allianceauth/-/releases)
- [Devloper Team](#developer-team)
- [Developer Team](#developer-team)
- [Contributing](#contributing)
## Overview
Alliance Auth (AA) is a web application that helps Eve Online organizations efficiently manage access to their applications and services.
Alliance Auth (AA) is a web site that helps Eve Online organizations efficiently manage access to applications and services.
Main features:
- Automatically grants or revokes user access to external applications / services (e.g. Discord, Mumble) and web apps (e.g. SRP requests) based on the user's current membership to [in-game organizations](https://allianceauth.readthedocs.io/en/latest/features/states/) and [groups](https://allianceauth.readthedocs.io/en/latest/features/groups/)
- Automatically grants or revokes user access to external services (e.g. Discord, Mumble) and web apps (e.g. SRP requests) based on the user's current membership to [in-game organizations](https://allianceauth.readthedocs.io/en/latest/features/core/states/) and [groups](https://allianceauth.readthedocs.io/en/latest/features/core/groups/)
- Provides a central web site where users can directly access web apps (e.g. SRP requests, Fleet Schedule) and manage their access to external services and groups.
- Includes a set of connectors (called ["services"](https://allianceauth.readthedocs.io/en/latest/installation/services/)) for integrating access management with many popular external applications / services like Discord, Mumble, Teamspeak 3, SMF and others
- Includes a set of connectors (called ["services"](https://allianceauth.readthedocs.io/en/latest/features/services/)) for integrating access management with many popular external applications / services like Discord, Mumble, Teamspeak 3, SMF and others
- Includes a set of web apps called ["plug-in apps"](https://allianceauth.readthedocs.io/en/latest/features/) which add many useful functions: fleet schedule, timer board, SRP request management, fleet activity tracker and character application management
- Includes a set of web [apps](https://allianceauth.readthedocs.io/en/latest/features/apps/) which add many useful functions, e.g.: fleet schedule, timer board, SRP request management, fleet activity tracker
- Can be easily extended with new services and plugin-apps. Many additional services and plugin-apps are provided by the community and can be found here: [Community Creations](https://gitlab.com/allianceauth/community-creations)
- Can be easily extended with additional services and apps. Many are provided by the community and can be found here: [Community Creations](https://gitlab.com/allianceauth/community-creations)
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [offical documentation](http://allianceauth.rtfd.io).
- Chinese :cn:, English :us:, German :de: and Spanish :es: localization
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](http://allianceauth.rtfd.io).
## Screenshot

View File

@@ -1,6 +1,6 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
__version__ = '2.6.2'
__version__ = '2.6.3'
NAME = 'Alliance Auth v%s' % __version__
default_app_config = 'allianceauth.apps.AllianceAuthConfig'

View File

@@ -506,7 +506,7 @@ class BaseOwnershipAdmin(admin.ModelAdmin):
'character',
)
search_fields = (
'user__user',
'user__username',
'character__character_name',
'character__corporation_name',
'character__alliance_name'

View File

@@ -2,10 +2,17 @@ from django.urls import reverse
def get_admin_change_view_url(obj: object) -> str:
"""returns URL to admin change view for given object"""
return reverse(
'admin:{}_{}_change'.format(
obj._meta.app_label,
type(obj).__name__.lower()
obj._meta.app_label, type(obj).__name__.lower()
),
args=(obj.pk,)
)
def get_admin_search_url(ModelClass: type) -> str:
"""returns URL to search URL for model of given object"""
return '{}{}/'.format(
reverse('admin:app_list', args=(ModelClass._meta.app_label,)),
ModelClass.__name__.lower()
)

View File

@@ -1,3 +1,4 @@
from urllib.parse import quote
from unittest.mock import patch
from django.conf import settings
@@ -6,8 +7,9 @@ from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import User as BaseUser, Group
from django.test import TestCase, RequestFactory, Client
from allianceauth.authentication.models import CharacterOwnership, State, \
OwnershipRecord
from allianceauth.authentication.models import (
CharacterOwnership, State, OwnershipRecord
)
from allianceauth.eveonline.models import (
EveCharacter, EveCorporationInfo, EveAllianceInfo
)
@@ -28,7 +30,7 @@ from ..admin import (
user_username,
update_main_character_model
)
from . import get_admin_change_view_url
from . import get_admin_change_view_url, get_admin_search_url
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
_has_auto_groups = True
@@ -175,6 +177,17 @@ def create_test_data():
return user_1, user_2, user_3, group_1, group_2
def make_generic_search_request(ModelClass: type, search_term: str):
User.objects.create_superuser(
username='superuser', password='secret', email='admin@example.com'
)
c = Client()
c.login(username='superuser', password='secret')
return c.get(
'%s?q=%s' % (get_admin_search_url(ModelClass), quote(search_term))
)
class TestCharacterOwnershipAdmin(TestCase):
@classmethod
@@ -197,6 +210,14 @@ class TestCharacterOwnershipAdmin(TestCase):
response = c.get(get_admin_change_view_url(ownership))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = CharacterOwnership.objects\
.filter(user=self.user_1)\
.first()
response = make_generic_search_request(type(obj), obj.user.username)
expected = 200
self.assertEqual(response.status_code, expected)
class TestOwnershipRecordAdmin(TestCase):
@@ -222,6 +243,12 @@ class TestOwnershipRecordAdmin(TestCase):
response = c.get(get_admin_change_view_url(ownership_record))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = OwnershipRecord.objects.first()
response = make_generic_search_request(type(obj), obj.user.username)
expected = 200
self.assertEqual(response.status_code, expected)
class TestStateAdmin(TestCase):
@@ -250,6 +277,11 @@ class TestStateAdmin(TestCase):
response = c.get(get_admin_change_view_url(member_state))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = State.objects.first()
response = make_generic_search_request(type(obj), obj.name)
expected = 200
self.assertEqual(response.status_code, expected)
class TestUserAdmin(TestCase):
@@ -541,3 +573,9 @@ class TestUserAdmin(TestCase):
c.login(username='superuser', password='secret')
response = c.get(get_admin_change_view_url(self.user_1))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = User.objects.first()
response = make_generic_search_request(type(obj), obj.username)
expected = 200
self.assertEqual(response.status_code, expected)

View File

@@ -8,7 +8,7 @@ from django.contrib.auth.models import User
from django.core import signing
from django.urls import reverse
from django.shortcuts import redirect, render
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from allianceauth.eveonline.models import EveCharacter
from esi.decorators import token_required
@@ -69,7 +69,10 @@ def main_character_change(request, token):
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}))
messages.error(
request,
_('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

View File

@@ -7,6 +7,7 @@ from .models import EveCorporationInfo
logger = logging.getLogger(__name__)
TASK_PRIORITY = 7
@shared_task
def update_corp(corp_id):
@@ -27,11 +28,12 @@ def update_character(character_id):
def run_model_update():
# update existing corp models
for corp in EveCorporationInfo.objects.all().values('corporation_id'):
update_corp.delay(corp['corporation_id'])
update_corp.apply_async(args=[corp['corporation_id']], priority=TASK_PRIORITY)
# update existing alliance models
for alliance in EveAllianceInfo.objects.all().values('alliance_id'):
update_alliance.delay(alliance['alliance_id'])
update_alliance.apply_async(args=[alliance['alliance_id']], priority=TASK_PRIORITY)
#update existing character models
for character in EveCharacter.objects.all().values('character_id'):
update_character.delay(character['character_id'])
update_character.apply_async(args=[character['character_id']], priority=TASK_PRIORITY)

View File

@@ -80,28 +80,28 @@ class TestTasks(TestCase):
character_name='character.name',
corporation_id='character.corp.id',
corporation_name='character.corp.name',
corporation_ticker='character.corp.ticker',
corporation_ticker='c.c.t', # max 5 chars
alliance_id='character.alliance.id',
alliance_name='character.alliance.name',
)
run_model_update()
self.assertEqual(mock_update_corp.delay.call_count, 1)
self.assertEqual(mock_update_corp.apply_async.call_count, 1)
self.assertEqual(
int(mock_update_corp.delay.call_args[0][0]),
int(mock_update_corp.apply_async.call_args[1]['args'][0]),
2345
)
self.assertEqual(mock_update_alliance.delay.call_count, 1)
self.assertEqual(mock_update_alliance.apply_async.call_count, 1)
self.assertEqual(
int(mock_update_alliance.delay.call_args[0][0]),
int(mock_update_alliance.apply_async.call_args[1]['args'][0]),
3456
)
self.assertEqual(mock_update_character.delay.call_count, 1)
self.assertEqual(mock_update_character.apply_async.call_count, 1)
self.assertEqual(
int(mock_update_character.delay.call_args[0][0]),
int(mock_update_character.apply_async.call_args[1]['args'][0]),
1234
)

View File

@@ -1,27 +1,53 @@
from django.contrib.auth.models import Group
from django.db.models import Q
import logging
from django.contrib.auth.models import Group, User
from django.db.models import Q, QuerySet
from allianceauth.authentication.models import State
logger = logging.getLogger(__name__)
class GroupManager:
def __init__(self):
pass
@classmethod
def get_joinable_groups_for_user(
cls, user: User, include_hidden = True
) -> QuerySet:
"""get groups a user could join incl. groups already joined"""
groups_qs = cls.get_joinable_groups(user.profile.state)
if not user.has_perm('groupmanagement.request_groups'):
groups_qs = groups_qs.filter(authgroup__public=True)
if not include_hidden:
groups_qs = groups_qs.filter(authgroup__hidden=False)
return groups_qs
@staticmethod
def get_joinable_groups(state):
return Group.objects.select_related('authgroup').exclude(authgroup__internal=True)\
def get_joinable_groups(state: State) -> QuerySet:
"""get groups that can be joined by user with given state"""
return Group.objects\
.select_related('authgroup')\
.exclude(authgroup__internal=True)\
.filter(Q(authgroup__states=state) | Q(authgroup__states=None))
@staticmethod
def get_all_non_internal_groups():
return Group.objects.select_related('authgroup').exclude(authgroup__internal=True)
def get_all_non_internal_groups() -> QuerySet:
"""get groups that are not internal"""
return Group.objects\
.select_related('authgroup')\
.exclude(authgroup__internal=True)
@staticmethod
def get_group_leaders_groups(user):
def get_group_leaders_groups(user: User):
return Group.objects.select_related('authgroup').filter(authgroup__group_leaders__in=[user]) | \
Group.objects.select_related('authgroup').filter(authgroup__group_leader_groups__in=user.groups.all())
@staticmethod
def joinable_group(group, state):
def joinable_group(group: Group, state: State) -> bool:
"""
Check if a group is a user/state joinable group, i.e.
not an internal group for Corp, Alliance, Members etc,
@@ -30,12 +56,15 @@ class GroupManager:
:param state: allianceauth.authentication.State object
:return: bool True if its joinable, False otherwise
"""
if len(group.authgroup.states.all()) != 0 and state not in group.authgroup.states.all():
if (len(group.authgroup.states.all()) != 0
and state not in group.authgroup.states.all()
):
return False
return not group.authgroup.internal
else:
return not group.authgroup.internal
@staticmethod
def check_internal_group(group):
def check_internal_group(group: Group) -> bool:
"""
Check if a group is auditable, i.e not an internal group
:param group: django.contrib.auth.models.Group object
@@ -44,20 +73,11 @@ class GroupManager:
return not group.authgroup.internal
@staticmethod
def check_internal_group(group):
"""
Check if a group is auditable, i.e not an internal group
:param group: django.contrib.auth.models.Group object
:return: bool True if it is auditable, false otherwise
"""
return not group.authgroup.internal
@staticmethod
def has_management_permission(user):
def has_management_permission(user: User) -> bool:
return user.has_perm('auth.group_management')
@classmethod
def can_manage_groups(cls, user):
def can_manage_groups(cls, user:User ) -> bool:
"""
For use with user_passes_test decorator.
Check if the user can manage groups. Either has the
@@ -71,7 +91,7 @@ class GroupManager:
return False
@classmethod
def can_manage_group(cls, user, group):
def can_manage_group(cls, user: User, group: Group) -> bool:
"""
Check user has permission to manage the given group
:param user: User object to test permission of

View File

@@ -15,7 +15,7 @@
<div class="panel-body">
<p>
<a class="btn btn-default" href="{% url 'groupmanagement:membership' %}" role="button">
Back
{% trans "Back" %}
</a>
</p>
{% if entries %}

View File

@@ -17,7 +17,7 @@
<div class="panel-body">
<p>
<a class="btn btn-default" href="{% url 'groupmanagement:membership' %}" role="button">
Back
{% trans "Back" %}
</a>
</p>
{% if group.user_set %}

View File

@@ -11,7 +11,7 @@
{% include 'groupmanagement/menu.html' %}
<div class="panel panel-default">
<div class="panel-heading">
Groups
{% trans "Groups" %}
</div>
<div class="panel-body">
{% if groups %}

View File

@@ -1,107 +0,0 @@
from unittest import mock
from django.test import TestCase
from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo, EveCharacter
from django.contrib.auth.models import User, Group
from allianceauth.groupmanagement.managers import GroupManager
from allianceauth.groupmanagement.signals import check_groups_on_state_change
class GroupManagementVisibilityTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('test')
AuthUtils.add_main_character(cls.user, 'test character', '1', corp_id='2', corp_name='test_corp', corp_ticker='TEST', alliance_id='3', alliance_name='TEST')
cls.user.profile.refresh_from_db()
cls.alliance = EveAllianceInfo.objects.create(alliance_id='3', alliance_name='test alliance', alliance_ticker='TEST', executor_corp_id='2')
cls.corp = EveCorporationInfo.objects.create(corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', alliance=cls.alliance, member_count=1)
cls.group1 = Group.objects.create(name='group1')
cls.group2 = Group.objects.create(name='group2')
cls.group3 = Group.objects.create(name='group3')
def setUp(self):
self.user.refresh_from_db()
def _refresh_user(self):
self.user = User.objects.get(pk=self.user.pk)
def test_get_group_leaders_groups(self):
self.group1.authgroup.group_leaders.add(self.user)
self.group2.authgroup.group_leader_groups.add(self.group1)
self._refresh_user()
groups = GroupManager.get_group_leaders_groups(self.user)
self.assertIn(self.group1, groups) #avail due to user
self.assertNotIn(self.group2, groups) #not avail due to group
self.assertNotIn(self.group3, groups) #not avail at all
self.user.groups.add(self.group1)
self._refresh_user()
groups = GroupManager.get_group_leaders_groups(self.user)
self.assertIn(self.group1, groups) #avail due to user
self.assertIn(self.group2, groups) #avail due to group1
self.assertNotIn(self.group3, groups) #not avail at all
def test_can_manage_group(self):
self.group1.authgroup.group_leaders.add(self.user)
self.user.groups.add(self.group1)
self._refresh_user()
self.assertTrue(GroupManager.can_manage_group(self.user, self.group1))
self.assertFalse(GroupManager.can_manage_group(self.user, self.group2))
self.assertFalse(GroupManager.can_manage_group(self.user, self.group3))
self.group2.authgroup.group_leader_groups.add(self.group1)
self.group1.authgroup.group_leaders.remove(self.user)
self._refresh_user()
self.assertFalse(GroupManager.can_manage_group(self.user, self.group1))
self.assertTrue(GroupManager.can_manage_group(self.user, self.group2))
self.assertFalse(GroupManager.can_manage_group(self.user, self.group3))
class GroupManagementStateTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('test')
AuthUtils.add_main_character(cls.user, 'test character', '1', corp_id='2', corp_name='test_corp', corp_ticker='TEST', alliance_id='3', alliance_name='TEST')
cls.user.profile.refresh_from_db()
cls.alliance = EveAllianceInfo.objects.create(alliance_id='3', alliance_name='test alliance', alliance_ticker='TEST', executor_corp_id='2')
cls.corp = EveCorporationInfo.objects.create(corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', alliance=cls.alliance, member_count=1)
cls.state_group = Group.objects.create(name='state_group')
cls.open_group = Group.objects.create(name='open_group')
cls.state = AuthUtils.create_state('test state', 500)
cls.state_group.authgroup.states.add(cls.state)
cls.state_group.authgroup.internal = False
cls.state_group.save()
def setUp(self):
self.user.refresh_from_db()
self.state.refresh_from_db()
def _refresh_user(self):
self.user = User.objects.get(pk=self.user.pk)
def _refresh_test_group(self):
self.state_group = Group.objects.get(pk=self.state_group.pk)
def test_drop_state_group(self):
self.user.groups.add(self.open_group)
self.user.groups.add(self.state_group)
self.assertEqual(self.user.profile.state.name, "Guest")
self.state.member_corporations.add(self.corp)
self._refresh_user()
self.assertEqual(self.user.profile.state, self.state)
groups = self.user.groups.all()
self.assertIn(self.state_group, groups) #keeps group
self.assertIn(self.open_group, groups) #public group unafected
self.state.member_corporations.clear()
self._refresh_user()
self.assertEqual(self.user.profile.state.name, "Guest")
groups = self.user.groups.all()
self.assertNotIn(self.state_group, groups) #looses group
self.assertIn(self.open_group, groups) #public group unafected

View File

@@ -0,0 +1,337 @@
from unittest.mock import Mock, patch
from django.contrib.auth.models import Group, User
from django.test import TestCase
from django.urls import reverse
from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo
from allianceauth.tests.auth_utils import AuthUtils
from ..models import AuthGroup
from ..managers import GroupManager
class MockUserNotAuthenticated():
def __init__(self):
self.is_authenticated = False
class GroupManagementVisibilityTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('test')
AuthUtils.add_main_character(
cls.user, 'test character', '1', corp_id='2', corp_name='test_corp', corp_ticker='TEST', alliance_id='3', alliance_name='TEST'
)
cls.user.profile.refresh_from_db()
cls.alliance = EveAllianceInfo.objects.create(alliance_id='3', alliance_name='test alliance', alliance_ticker='TEST', executor_corp_id='2')
cls.corp = EveCorporationInfo.objects.create(
corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', alliance=cls.alliance, member_count=1
)
cls.group1 = Group.objects.create(name='group1')
cls.group2 = Group.objects.create(name='group2')
cls.group3 = Group.objects.create(name='group3')
def setUp(self):
self.user.refresh_from_db()
def _refresh_user(self):
self.user = User.objects.get(pk=self.user.pk)
def test_get_group_leaders_groups(self):
self.group1.authgroup.group_leaders.add(self.user)
self.group2.authgroup.group_leader_groups.add(self.group1)
self._refresh_user()
groups = GroupManager.get_group_leaders_groups(self.user)
self.assertIn(self.group1, groups) #avail due to user
self.assertNotIn(self.group2, groups) #not avail due to group
self.assertNotIn(self.group3, groups) #not avail at all
self.user.groups.add(self.group1)
self._refresh_user()
groups = GroupManager.get_group_leaders_groups(self.user)
def test_can_manage_group(self):
self.group1.authgroup.group_leaders.add(self.user)
self.user.groups.add(self.group1)
self._refresh_user()
self.assertTrue(GroupManager.can_manage_group(self.user, self.group1))
self.assertFalse(GroupManager.can_manage_group(self.user, self.group2))
self.assertFalse(GroupManager.can_manage_group(self.user, self.group3))
self.group2.authgroup.group_leader_groups.add(self.group1)
self.group1.authgroup.group_leaders.remove(self.user)
self._refresh_user()
self.assertFalse(GroupManager.can_manage_group(self.user, self.group1))
self.assertTrue(GroupManager.can_manage_group(self.user, self.group2))
self.assertFalse(GroupManager.can_manage_group(self.user, self.group3))
class TestGroupManager(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# group 1
cls.group_default = Group.objects.create(name='default')
cls.group_default.authgroup.description = 'Default Group'
cls.group_default.authgroup.internal = False
cls.group_default.authgroup.hidden = False
cls.group_default.authgroup.save()
# group 2
cls.group_internal = Group.objects.create(name='internal')
cls.group_internal.authgroup.description = 'Internal Group'
cls.group_internal.authgroup.internal = True
cls.group_internal.authgroup.save()
# group 3
cls.group_hidden = Group.objects.create(name='hidden')
cls.group_hidden.authgroup.description = 'Hidden Group'
cls.group_hidden.authgroup.internal = False
cls.group_hidden.authgroup.hidden = True
cls.group_hidden.authgroup.save()
# group 4
cls.group_open = Group.objects.create(name='open')
cls.group_open.authgroup.description = 'Open Group'
cls.group_open.authgroup.internal = False
cls.group_open.authgroup.hidden = False
cls.group_open.authgroup.open = True
cls.group_open.authgroup.save()
# group 5
cls.group_public_1 = Group.objects.create(name='public 1')
cls.group_public_1.authgroup.description = 'Public Group 1'
cls.group_public_1.authgroup.internal = False
cls.group_public_1.authgroup.hidden = False
cls.group_public_1.authgroup.public = True
cls.group_public_1.authgroup.save()
# group 6
cls.group_public_2 = Group.objects.create(name='public 2')
cls.group_public_2.authgroup.description = 'Public Group 2'
cls.group_public_2.authgroup.internal = False
cls.group_public_2.authgroup.hidden = True
cls.group_public_2.authgroup.open = True
cls.group_public_2.authgroup.public = True
cls.group_public_2.authgroup.save()
# group 7
cls.group_default_member = Group.objects.create(name='default members')
cls.group_default_member.authgroup.description = \
'Default Group for members only'
cls.group_default_member.authgroup.internal = False
cls.group_default_member.authgroup.hidden = False
cls.group_default_member.authgroup.open = False
cls.group_default_member.authgroup.public = False
cls.group_default_member.authgroup.states.add(
AuthUtils.get_member_state()
)
cls.group_default_member.authgroup.save()
def setUp(self):
self.user = AuthUtils.create_user('Bruce Wayne')
def test_get_joinable_group_member(self):
result = GroupManager.get_joinable_groups(
AuthUtils.get_member_state()
)
expected = {
self.group_default,
self.group_hidden,
self.group_open,
self.group_public_1,
self.group_public_2,
self.group_default_member
}
self.assertSetEqual(set(result), expected)
def test_get_joinable_group_guest(self):
result = GroupManager.get_joinable_groups(
AuthUtils.get_guest_state()
)
expected = {
self.group_default,
self.group_hidden,
self.group_open,
self.group_public_1,
self.group_public_2
}
self.assertSetEqual(set(result), expected)
def test_joinable_group_member(self):
member_state = AuthUtils.get_member_state()
for x in [
self.group_default,
self.group_hidden,
self.group_open,
self.group_public_1,
self.group_public_2,
self.group_default_member
]:
self.assertTrue(GroupManager.joinable_group(x, member_state))
for x in [
self.group_internal,
]:
self.assertFalse(GroupManager.joinable_group(x, member_state))
def test_joinable_group_guest(self):
guest_state = AuthUtils.get_guest_state()
for x in [
self.group_default,
self.group_hidden,
self.group_open,
self.group_public_1,
self.group_public_2
]:
self.assertTrue(GroupManager.joinable_group(x, guest_state))
for x in [
self.group_internal,
self.group_default_member
]:
self.assertFalse(GroupManager.joinable_group(x, guest_state))
def test_get_all_non_internal_groups(self):
result = GroupManager.get_all_non_internal_groups()
expected = {
self.group_default,
self.group_hidden,
self.group_open,
self.group_public_1,
self.group_public_2,
self.group_default_member
}
self.assertSetEqual(set(result), expected)
def test_check_internal_group(self):
self.assertTrue(
GroupManager.check_internal_group(self.group_default)
)
self.assertFalse(
GroupManager.check_internal_group(self.group_internal)
)
def test_get_joinable_groups_for_user_no_permission(self):
AuthUtils.assign_state(self.user, AuthUtils.get_guest_state())
result = GroupManager.get_joinable_groups_for_user(self.user)
expected= {self.group_public_1, self.group_public_2}
self.assertSetEqual(set(result), expected)
def test_get_joinable_groups_for_user_guest_w_permission_(self):
AuthUtils.assign_state(self.user, AuthUtils.get_guest_state())
AuthUtils.add_permission_to_user_by_name(
'groupmanagement.request_groups', self.user
)
result = GroupManager.get_joinable_groups_for_user(self.user)
expected = {
self.group_default,
self.group_hidden,
self.group_open,
self.group_public_1,
self.group_public_2
}
self.assertSetEqual(set(result), expected)
def test_get_joinable_groups_for_user_member_w_permission(self):
AuthUtils.assign_state(self.user, AuthUtils.get_member_state(), True)
AuthUtils.add_permission_to_user_by_name(
'groupmanagement.request_groups', self.user
)
result = GroupManager.get_joinable_groups_for_user(self.user)
expected = {
self.group_default,
self.group_hidden,
self.group_open,
self.group_public_1,
self.group_public_2,
self.group_default_member
}
self.assertSetEqual(set(result), expected)
def test_get_joinable_groups_for_user_member_w_permission_no_hidden(self):
AuthUtils.assign_state(self.user, AuthUtils.get_member_state(), True)
AuthUtils.add_permission_to_user_by_name(
'groupmanagement.request_groups', self.user
)
result = GroupManager.get_joinable_groups_for_user(
self.user, include_hidden=False
)
expected = {
self.group_default,
self.group_open,
self.group_public_1,
self.group_default_member
}
self.assertSetEqual(set(result), expected)
def test_has_management_permission(self):
user = AuthUtils.create_user('Clark Kent')
AuthUtils.add_permission_to_user_by_name(
'auth.group_management', user
)
self.assertTrue(GroupManager.has_management_permission(user))
def test_can_manage_groups_no_perm_no_group(self):
user = AuthUtils.create_user('Clark Kent')
self.assertFalse(GroupManager.can_manage_groups(user))
def test_can_manage_groups_user_not_authenticated(self):
user = MockUserNotAuthenticated()
self.assertFalse(GroupManager.can_manage_groups(user))
def test_can_manage_groups_has_perm(self):
user = AuthUtils.create_user('Clark Kent')
AuthUtils.add_permission_to_user_by_name(
'auth.group_management', user
)
self.assertTrue(GroupManager.can_manage_groups(user))
def test_can_manage_groups_no_perm_leads_group(self):
user = AuthUtils.create_user('Clark Kent')
self.group_default.authgroup.group_leaders.add(user)
self.assertTrue(GroupManager.can_manage_groups(user))
def test_can_manage_group_no_perm_no_group(self):
user = AuthUtils.create_user('Clark Kent')
self.assertFalse(
GroupManager.can_manage_group(user, self.group_default)
)
def test_can_manage_group_has_perm(self):
user = AuthUtils.create_user('Clark Kent')
AuthUtils.add_permission_to_user_by_name(
'auth.group_management', user
)
self.assertTrue(
GroupManager.can_manage_group(user, self.group_default)
)
def test_can_manage_group_no_perm_leads_correct_group(self):
user = AuthUtils.create_user('Clark Kent')
self.group_default.authgroup.group_leaders.add(user)
self.assertTrue(
GroupManager.can_manage_group(user, self.group_default)
)
def test_can_manage_group_no_perm_leads_other_group(self):
user = AuthUtils.create_user('Clark Kent')
self.group_hidden.authgroup.group_leaders.add(user)
self.assertFalse(
GroupManager.can_manage_group(user, self.group_default)
)
def test_can_manage_group_user_not_authenticated(self):
user = MockUserNotAuthenticated()
self.assertFalse(
GroupManager.can_manage_group(user, self.group_default)
)

View File

@@ -0,0 +1,167 @@
from unittest import mock
from django.contrib.auth.models import User, Group
from django.test import TestCase
from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.eveonline.models import (
EveCorporationInfo, EveAllianceInfo, EveCharacter
)
from ..models import GroupRequest, RequestLog
def create_testdata():
# clear DB
User.objects.all().delete()
Group.objects.all().delete()
EveCharacter.objects.all().delete()
EveCorporationInfo.objects.all().delete()
EveAllianceInfo.objects.all().delete()
# group 1
group = Group.objects.create(name='Superheros')
group.authgroup.description = 'Default Group'
group.authgroup.internal = False
group.authgroup.hidden = False
group.authgroup.save()
# user 1
user_1 = AuthUtils.create_user('Bruce Wayne')
AuthUtils.add_main_character_2(
user_1,
name='Bruce Wayne',
character_id=1001,
corp_id=2001,
corp_name='Wayne Technologies'
)
user_1.groups.add(group)
group.authgroup.group_leaders.add(user_1)
# user 2
user_2 = AuthUtils.create_user('Clark Kent')
AuthUtils.add_main_character_2(
user_2,
name='Clark Kent',
character_id=1002,
corp_id=2002,
corp_name='Wayne Technologies'
)
return group, user_1, user_2
class TestGroupRequest(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.group, cls.user_1, _ = create_testdata()
def test_main_char(self):
group_request = GroupRequest.objects.create(
status='Pending',
user=self.user_1,
group=self.group
)
expected = self.user_1.profile.main_character
self.assertEqual(group_request.main_char, expected)
def test_str(self):
group_request = GroupRequest.objects.create(
status='Pending',
user=self.user_1,
group=self.group
)
expected = 'Bruce Wayne:Superheros'
self.assertEqual(str(group_request), expected)
class TestRequestLog(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.group, cls.user_1, cls.user_2 = create_testdata()
def test_requestor(self):
request_log = RequestLog.objects.create(
group=self.group,
request_info='Clark Kent:Superheros',
request_actor=self.user_1
)
expected = 'Clark Kent'
self.assertEqual(request_log.requestor(), expected)
def test_type_to_str_removed(self):
request_log = RequestLog.objects.create(
request_type=None,
group=self.group,
request_info='Clark Kent:Superheros',
request_actor=self.user_1
)
expected = 'Removed'
self.assertEqual(request_log.type_to_str(), expected)
def test_type_to_str_leave(self):
request_log = RequestLog.objects.create(
request_type=True,
group=self.group,
request_info='Clark Kent:Superheros',
request_actor=self.user_1
)
expected = 'Leave'
self.assertEqual(request_log.type_to_str(), expected)
def test_type_to_str_join(self):
request_log = RequestLog.objects.create(
request_type=False,
group=self.group,
request_info='Clark Kent:Superheros',
request_actor=self.user_1
)
expected = 'Join'
self.assertEqual(request_log.type_to_str(), expected)
def test_action_to_str_accept(self):
request_log = RequestLog.objects.create(
group=self.group,
request_info='Clark Kent:Superheros',
request_actor=self.user_1,
action = True
)
expected = 'Accept'
self.assertEqual(request_log.action_to_str(), expected)
def test_action_to_str_reject(self):
request_log = RequestLog.objects.create(
group=self.group,
request_info='Clark Kent:Superheros',
request_actor=self.user_1,
action = False
)
expected = 'Reject'
self.assertEqual(request_log.action_to_str(), expected)
def test_req_char(self):
request_log = RequestLog.objects.create(
group=self.group,
request_info='Clark Kent:Superheros',
request_actor=self.user_1,
action = False
)
expected = self.user_2.profile.main_character
self.assertEqual(request_log.req_char(), expected)
class TestAuthGroup(TestCase):
def test_str(self):
group = Group.objects.create(name='Superheros')
group.authgroup.description = 'Default Group'
group.authgroup.internal = False
group.authgroup.hidden = False
group.authgroup.save()
expected = 'Superheros'
self.assertEqual(str(group.authgroup), expected)

View File

@@ -0,0 +1,61 @@
from unittest import mock
from django.test import TestCase
from django.contrib.auth.models import User, Group
from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo
from allianceauth.tests.auth_utils import AuthUtils
from ..signals import check_groups_on_state_change
class GroupManagementStateTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('test')
AuthUtils.add_main_character(
cls.user, 'test character', '1', corp_id='2', corp_name='test_corp', corp_ticker='TEST', alliance_id='3', alliance_name='TEST'
)
cls.user.profile.refresh_from_db()
cls.alliance = EveAllianceInfo.objects.create(
alliance_id='3', alliance_name='test alliance', alliance_ticker='TEST', executor_corp_id='2'
)
cls.corp = EveCorporationInfo.objects.create(
corporation_id='2', corporation_name='test corp', corporation_ticker='TEST', alliance=cls.alliance, member_count=1
)
cls.state_group = Group.objects.create(name='state_group')
cls.open_group = Group.objects.create(name='open_group')
cls.state = AuthUtils.create_state('test state', 500)
cls.state_group.authgroup.states.add(cls.state)
cls.state_group.authgroup.internal = False
cls.state_group.save()
def setUp(self):
self.user.refresh_from_db()
self.state.refresh_from_db()
def _refresh_user(self):
self.user = User.objects.get(pk=self.user.pk)
def _refresh_test_group(self):
self.state_group = Group.objects.get(pk=self.state_group.pk)
def test_drop_state_group(self):
self.user.groups.add(self.open_group)
self.user.groups.add(self.state_group)
self.assertEqual(self.user.profile.state.name, "Guest")
self.state.member_corporations.add(self.corp)
self._refresh_user()
self.assertEqual(self.user.profile.state, self.state)
groups = self.user.groups.all()
self.assertIn(self.state_group, groups) #keeps group
self.assertIn(self.open_group, groups) #public group unafected
self.state.member_corporations.clear()
self._refresh_user()
self.assertEqual(self.user.profile.state.name, "Guest")
groups = self.user.groups.all()
self.assertNotIn(self.state_group, groups) #looses group
self.assertIn(self.open_group, groups) #public group unafected

View File

@@ -0,0 +1,22 @@
from unittest.mock import Mock, patch
from django.test import RequestFactory, TestCase
from django.urls import reverse
from allianceauth.tests.auth_utils import AuthUtils
from esi.models import Token
from .. import views
class TestViews(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.user = AuthUtils.create_user('Bruce Wayne')
def test_groups_view_can_load(self):
request = self.factory.get(reverse('groupmanagement:groups'))
request.user = self.user
response = views.groups_view(request)
self.assertEqual(response.status_code, 200)

View File

@@ -1,5 +1,6 @@
import logging
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
@@ -10,12 +11,12 @@ from django.db.models import Count
from django.http import Http404
from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import ugettext_lazy as _
from .managers import GroupManager
from .models import GroupRequest, RequestLog
from allianceauth.notifications import notify
from django.conf import settings
from .managers import GroupManager
from .models import GroupRequest, RequestLog
logger = logging.getLogger(__name__)
@@ -235,7 +236,7 @@ def group_reject_request(request, group_request_id):
raise p
except:
messages.error(request, _('An unhandled error occurred while processing the application from %(mainchar)s to %(group)s.') % {"mainchar": group_request.main_char, "group": group_request.group})
logger.exception("Unhandled exception occured while user %s attempting to reject group request id %s" % (
logger.exception("Unhandled exception occurred while user %s attempting to reject group request id %s" % (
request.user, group_request_id))
pass
@@ -269,9 +270,9 @@ def group_leave_accept_request(request, group_request_id):
(request.user, group_request_id))
raise p
except:
messages.error(request, _('An unhandled error occured while processing the application from %(mainchar)s to leave %(group)s.') % {
messages.error(request, _('An unhandled error occurred while processing the application from %(mainchar)s to leave %(group)s.') % {
"mainchar": group_request.main_char, "group": group_request.group})
logger.exception("Unhandled exception occured while user %s attempting to accept group leave request id %s" % (
logger.exception("Unhandled exception occurred while user %s attempting to accept group leave request id %s" % (
request.user, group_request_id))
pass
@@ -303,9 +304,9 @@ def group_leave_reject_request(request, group_request_id):
(request.user, group_request_id))
raise p
except:
messages.error(request, _('An unhandled error occured while processing the application from %(mainchar)s to leave %(group)s.') % {
messages.error(request, _('An unhandled error occurred while processing the application from %(mainchar)s to leave %(group)s.') % {
"mainchar": group_request.main_char, "group": group_request.group})
logger.exception("Unhandled exception occured while user %s attempting to reject group leave request id %s" % (
logger.exception("Unhandled exception occurred while user %s attempting to reject group leave request id %s" % (
request.user, group_request_id))
pass
@@ -315,24 +316,23 @@ def group_leave_reject_request(request, group_request_id):
@login_required
def groups_view(request):
logger.debug("groups_view called by user %s" % request.user)
groups_qs = GroupManager.get_joinable_groups_for_user(
request.user, include_hidden=False
)
groups_qs = groups_qs.order_by('name')
groups = []
for group in groups_qs:
group_request = GroupRequest.objects\
.filter(user=request.user)\
.filter(group=group)
groups.append({
'group': group,
'request': group_request[0] if group_request else None
})
group_query = GroupManager.get_joinable_groups(request.user.profile.state)
if not request.user.has_perm('groupmanagement.request_groups'):
# Filter down to public groups only for non-members
group_query = group_query.filter(authgroup__public=True)
logger.debug("Not a member, only public groups will be available")
for group in group_query:
# Exclude hidden
if not group.authgroup.hidden:
group_request = GroupRequest.objects.filter(user=request.user).filter(group=group)
groups.append({'group': group, 'request': group_request[0] if group_request else None})
render_items = {'groups': groups}
return render(request, 'groupmanagement/groups.html', context=render_items)
context = {'groups': groups}
return render(request, 'groupmanagement/groups.html', context=context)
@login_required
@@ -349,13 +349,13 @@ def group_request_add(request, group_id):
# User is already a member of this group.
logger.warning("User %s attempted to join group id %s but they are already a member." %
(request.user, group_id))
messages.warning(request, "You are already a member of that group.")
messages.warning(request, _("You are already a member of that group."))
return redirect('groupmanagement:groups')
if not request.user.has_perm('groupmanagement.request_groups') and not group.authgroup.public:
# Does not have the required permission, trying to join a non-public group
logger.warning("User %s attempted to join group id %s but it is not a public group" %
(request.user, group_id))
messages.warning(request, "You cannot join that group")
messages.warning(request, _("You cannot join that group"))
return redirect('groupmanagement:groups')
if group.authgroup.open:
logger.info("%s joining %s as is an open group" % (request.user, group))
@@ -364,7 +364,7 @@ def group_request_add(request, group_id):
req = GroupRequest.objects.filter(user=request.user, group=group)
if len(req) > 0:
logger.info("%s attempted to join %s but already has an open application" % (request.user, group))
messages.warning(request, "You already have a pending application for that group.")
messages.warning(request, _("You already have a pending application for that group."))
return redirect("groupmanagement:groups")
grouprequest = GroupRequest()
grouprequest.status = _('Pending')
@@ -398,7 +398,7 @@ def group_request_leave(request, group_id):
req = GroupRequest.objects.filter(user=request.user, group=group)
if len(req) > 0:
logger.info("%s attempted to leave %s but already has an pending leave request." % (request.user, group))
messages.warning(request, "You already have a pending leave request for that group.")
messages.warning(request, _("You already have a pending leave request for that group."))
return redirect("groupmanagement:groups")
if getattr(settings, 'AUTO_LEAVE', False):
logger.info("%s leaving joinable group %s due to auto_leave" % (request.user, group))

View File

@@ -4,18 +4,18 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
# Translators:
# Rounon Dax <rounon.dax@terra-nanotech.de>, 2020
# Erik Kalkoken <erik.kalkoken@gmail.com>, 2020
# Joel Falknau <ozirascal@gmail.com>, 2020
# Rounon Dax <rounon.dax@terra-nanotech.de>, 2020
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-04 23:04+0000\n"
"POT-Creation-Date: 2020-04-02 03:23+0000\n"
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2020\n"
"Last-Translator: Rounon Dax <rounon.dax@terra-nanotech.de>, 2020\n"
"Language-Team: German (https://www.transifex.com/alliance-auth/teams/107430/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -71,9 +71,7 @@ msgid "Change Main"
msgstr "Hauptcharakter ändern"
#: allianceauth/authentication/templates/authentication/dashboard.html:97
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgid "Group Memberships"
msgstr "Gruppen"
#: allianceauth/authentication/templates/authentication/dashboard.html:117
@@ -140,32 +138,41 @@ msgstr "Euer IT Team"
msgid "Submit"
msgstr "Absenden"
#: allianceauth/authentication/views.py:39
#: allianceauth/authentication/views.py:74
#, python-format
msgid ""
"Cannot change main character to %(char)s: character owned by a different "
"account."
msgstr ""
"Der Haputcharakter kann nicht zu %(char)s geändert werden. Dieser Charakter "
"gehört zu einem anderen Konto."
#: allianceauth/authentication/views.py:80
#, python-format
msgid "Changed main character to %(char)s"
msgstr "Haupcharakter geändert zu %(char)s"
#: allianceauth/authentication/views.py:48
#: allianceauth/authentication/views.py:89
#, python-format
msgid "Added %(name)s to your account."
msgstr "%(name)s zu Deinem Konto hinzugefügt."
#: allianceauth/authentication/views.py:50
#: allianceauth/authentication/views.py:91
#, 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:89
#: allianceauth/authentication/views.py:130
msgid "Unable to authenticate as the selected character."
msgstr "Authentifizierung mit dem ausgewählten Charakter nicht möglich."
#: allianceauth/authentication/views.py:107
#: allianceauth/authentication/views.py:148
msgid "Registration token has expired."
msgstr "Token zur Registrierung ist abgelaufen."
#: allianceauth/authentication/views.py:159
#: allianceauth/authentication/views.py:200
msgid ""
"Sent confirmation email. Please follow the link to confirm your email "
"address."
@@ -173,12 +180,12 @@ msgstr ""
"Bestätigungsmail gesendet. Bitte folge dem Link in der E-Mail zur "
"Bestätigung."
#: allianceauth/authentication/views.py:164
#: allianceauth/authentication/views.py:205
msgid "Confirmed your email address. Please login to continue."
msgstr ""
"Deine E-Mail Adresse wurde bestätigt. Bitte log Dich ein um fortzufahren."
#: allianceauth/authentication/views.py:169
#: allianceauth/authentication/views.py:210
msgid "Registraion of new accounts it not allowed at this time."
msgstr "Registrierung von neuen Konten ist zur Zeit nicht erlaubt."
@@ -540,6 +547,12 @@ msgstr "FAT-Link ist abgelaufen."
msgid "Audit Log"
msgstr "Protokoll"
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:18
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:20
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:13
msgid "Back"
msgstr "Zurück"
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:25
msgid "Date/Time"
msgstr "Datum/Uhrzeit"
@@ -599,6 +612,12 @@ msgstr "Keine Gruppenmitglieder vorhanden."
msgid "Groups Membership"
msgstr "Gruppenmitgliedschaft"
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:14
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgstr "Gruppen"
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:23
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
msgid "Description"
@@ -718,26 +737,26 @@ msgstr "Gruppenanfragen"
msgid "Group Membership"
msgstr "Gruppenmitgliedschaft"
#: allianceauth/groupmanagement/views.py:164
#: 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:166
#: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group"
msgstr "Benutzer existiert nicht in dieser Gruppe"
#: allianceauth/groupmanagement/views.py:169
#: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist"
msgstr "Gruppe existiert nicht"
#: allianceauth/groupmanagement/views.py:196
#: allianceauth/groupmanagement/views.py:198
#, python-format
msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "Beitrittsgesuch von %(mainchar)s zur Gruppe %(group)s zugestimmt."
#: allianceauth/groupmanagement/views.py:203
#: allianceauth/groupmanagement/views.py:236
#: allianceauth/groupmanagement/views.py:205
#: allianceauth/groupmanagement/views.py:238
#, python-format
msgid ""
"An unhandled error occurred while processing the application from "
@@ -746,37 +765,46 @@ msgstr ""
"Bei der Bearbeitung des Beitrittsgesuchs von %(mainchar)s zur Gruppe "
"%(group)s ist ein unbehandelter Fehler aufgetreten."
#: allianceauth/groupmanagement/views.py:229
#: allianceauth/groupmanagement/views.py:231
#, python-format
msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "Beitrittsgesuch von %(mainchar)s zur Gruppe %(group)s abgelehnt."
#: allianceauth/groupmanagement/views.py:265
#: allianceauth/groupmanagement/views.py:267
#, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "Austrittsgesuch von %(mainchar)s für Gruppe %(group)s akzeptiert."
#: allianceauth/groupmanagement/views.py:271
#: allianceauth/groupmanagement/views.py:305
#: allianceauth/groupmanagement/views.py:273
#: allianceauth/groupmanagement/views.py:307
#, python-format
msgid ""
"An unhandled error occured while processing the application from "
"An unhandled error occurred while processing the application from "
"%(mainchar)s to leave %(group)s."
msgstr ""
"Bei der Bearbeitung des Austrittsgesuchs von %(mainchar)s für Gruppe "
"%(group)s ist ein unbehandelter Fehler aufgetreten."
#: allianceauth/groupmanagement/views.py:298
#: allianceauth/groupmanagement/views.py:300
#, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "Austrittsgesuch von %(mainchar)s für Gruppe %(group)s abgelehnt."
#: allianceauth/groupmanagement/views.py:345
#: allianceauth/groupmanagement/views.py:346
#: allianceauth/groupmanagement/views.py:358
msgid "You cannot join that group"
msgstr "Du kannst dieser Gruppe nicht beitreten"
#: allianceauth/groupmanagement/views.py:369
#: allianceauth/groupmanagement/views.py:407
#: allianceauth/groupmanagement/views.py:352
msgid "You are already a member of that group."
msgstr "Du bist bereits Mitglied dieser Gruppe."
#: allianceauth/groupmanagement/views.py:367
msgid "You already have a pending application for that group."
msgstr "Du hast Dich bereits für diese Gruppe beworben."
#: allianceauth/groupmanagement/views.py:370
#: allianceauth/groupmanagement/views.py:408
#: allianceauth/hrapplications/templates/hrapplications/management.html:37
#: allianceauth/hrapplications/templates/hrapplications/management.html:72
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
@@ -788,20 +816,24 @@ msgstr "Du kannst dieser Gruppe nicht beitreten"
msgid "Pending"
msgstr "Beantragt"
#: allianceauth/groupmanagement/views.py:375
#: allianceauth/groupmanagement/views.py:376
#, python-format
msgid "Applied to group %(group)s."
msgstr "Beitritt zur Gruppe %(group)s beantragt."
#: allianceauth/groupmanagement/views.py:386
#: allianceauth/groupmanagement/views.py:387
msgid "You cannot leave that group"
msgstr "Du kannst diese Gruppe nicht verlassen"
#: allianceauth/groupmanagement/views.py:391
#: allianceauth/groupmanagement/views.py:392
msgid "You are not a member of that group"
msgstr "Du bist kein Mitglied dieser Gruppe"
#: allianceauth/groupmanagement/views.py:413
#: allianceauth/groupmanagement/views.py:401
msgid "You already have a pending leave request for that group."
msgstr "Du hast bereits ein ausstehendes Austrittsgesuch für diese Gruppe."
#: allianceauth/groupmanagement/views.py:414
#, python-format
msgid "Applied to leave group %(group)s."
msgstr "Austrittsgesuch für Gruppe %(group)s gesendet."
@@ -1147,10 +1179,6 @@ msgstr "Änderungen für Operation timer %(opname)s gespeichert."
msgid "Permissions Audit"
msgstr "Berechtigungsprüfung"
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:13
msgid "Back"
msgstr "Zurück"
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:22
msgid "User / Character"
msgstr "Benutzer / Character"
@@ -1196,6 +1224,18 @@ msgstr "Nutzer"
msgid "States"
msgstr "Status"
#: allianceauth/services/abstract.py:72
msgid "That service account already exists"
msgstr "Dieses Dienstkonto existiert bereits"
#: allianceauth/services/abstract.py:104
msgid "Successfully set your {} password"
msgstr "Dein {} Passwort wurde erfolgreich gesetzt"
#: allianceauth/services/auth_hooks.py:11
msgid "Services"
msgstr "Dienste"
#: allianceauth/services/forms.py:6
msgid "Name of Fleet:"
msgstr "SRP Flotte erstellen:"
@@ -1260,15 +1300,85 @@ msgstr "Passwort muss mindestens 8 Zeichen lang sein"
msgid "Link Discord Server"
msgstr "Verbinde Discord Server"
#: allianceauth/services/modules/openfire/forms.py:7
msgid "Message"
msgstr "Nachricht"
#: allianceauth/services/modules/discord/views.py:26
msgid "Deactivated Discord account."
msgstr "Discord Konto deaktiviert."
#: allianceauth/services/modules/discord/views.py:29
#: allianceauth/services/modules/discord/views.py:41
#: allianceauth/services/modules/discord/views.py:65
msgid "An error occurred while processing your Discord account."
msgstr "Es gab einen Fehler bei der Verarbeitung Deines Discord Kontos."
#: allianceauth/services/modules/discord/views.py:62
msgid "Activated Discord account."
msgstr "Discord Konto aktiviert."
#: allianceauth/services/modules/discourse/views.py:37
msgid "You are not authorized to access Discourse."
msgstr "Du bist nicht autorisiert auf Discorse zuzugreifen."
#: allianceauth/services/modules/discourse/views.py:42
msgid "You must have a main character set to access Discourse."
msgstr ""
"Du musst einen Hauptcharakter gesetzt haben um auf Discourse zuzugreifen."
#: allianceauth/services/modules/discourse/views.py:52
msgid ""
"No SSO payload or signature. Please contact support if this problem "
"persists."
msgstr ""
"Keine SSO-Nutzdaten oder Signaturen. Bitte wenden Sie sich an den Support, "
"wenn das Problem weiterhin besteht."
#: allianceauth/services/modules/discourse/views.py:62
#: allianceauth/services/modules/discourse/views.py:70
msgid "Invalid payload. Please contact support if this problem persists."
msgstr ""
"Ungültige Nutzdaten. Bitte wenden Sie sich an den Support, wenn das Problem "
"weiterhin besteht."
#: allianceauth/services/modules/ips4/views.py:31
msgid "Activated IPSuite4 account."
msgstr "IP4Suite Konto aktiviert."
#: allianceauth/services/modules/ips4/views.py:40
#: allianceauth/services/modules/ips4/views.py:62
#: allianceauth/services/modules/ips4/views.py:83
#: allianceauth/services/modules/ips4/views.py:103
msgid "An error occurred while processing your IPSuite4 account."
msgstr "Es gab einen Fehler bei der Verarbeitung Deines IPSuite4 Kontos."
#: allianceauth/services/modules/ips4/views.py:53
msgid "Reset IPSuite4 password."
msgstr "IPSuite4 Passwort zurücksetzen."
#: allianceauth/services/modules/ips4/views.py:80
msgid "Set IPSuite4 password."
msgstr "Setze IPSuite4 Passwort."
#: allianceauth/services/modules/ips4/views.py:100
msgid "Deactivated IPSuite4 account."
msgstr "IP4Suite Konto deaktiviert."
#: allianceauth/services/modules/openfire/auth_hooks.py:26
msgid "Jabber"
msgstr "Jabber"
#: allianceauth/services/modules/openfire/auth_hooks.py:78
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:6
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:11
msgid "Jabber Broadcast"
msgstr "Jabber Übertragung"
#: allianceauth/services/modules/openfire/auth_hooks.py:91
msgid "Fleet Broadcast Formatter"
msgstr "Flotten Ping Formatierung"
#: allianceauth/services/modules/openfire/forms.py:7
msgid "Message"
msgstr "Nachricht"
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:17
msgid "Broadcast Sent!!"
msgstr "Übertragung gesendet!!"
@@ -1277,6 +1387,76 @@ msgstr "Übertragung gesendet!!"
msgid "Broadcast"
msgstr "Übertragungen"
#: allianceauth/services/modules/openfire/views.py:35
msgid "Activated jabber account."
msgstr "Jabber Konto aktiviert."
#: allianceauth/services/modules/openfire/views.py:44
#: allianceauth/services/modules/openfire/views.py:57
#: allianceauth/services/modules/openfire/views.py:78
#: allianceauth/services/modules/openfire/views.py:151
msgid "An error occurred while processing your jabber account."
msgstr "Es gab einen Fehler bei der Verarbeitung Deines Jabber Kontos."
#: allianceauth/services/modules/openfire/views.py:70
msgid "Reset jabber password."
msgstr "Jabber Passwort zurücksetzen."
#: allianceauth/services/modules/openfire/views.py:119
#, python-format
msgid "Sent jabber broadcast to %s"
msgstr "Sende Jabber Durchsage an %s"
#: allianceauth/services/modules/openfire/views.py:148
msgid "Set jabber password."
msgstr "Setze Jabber Passwort."
#: allianceauth/services/modules/phpbb3/views.py:34
msgid "Activated forum account."
msgstr "Forum Konto aktiviert."
#: allianceauth/services/modules/phpbb3/views.py:43
#: allianceauth/services/modules/phpbb3/views.py:57
#: allianceauth/services/modules/phpbb3/views.py:80
#: allianceauth/services/modules/phpbb3/views.py:103
msgid "An error occurred while processing your forum account."
msgstr "Es gab einen Fehler bei der Verarbeitung Deines Forum Kontos."
#: allianceauth/services/modules/phpbb3/views.py:54
msgid "Deactivated forum account."
msgstr "Forum Konto deaktiviert."
#: allianceauth/services/modules/phpbb3/views.py:71
msgid "Reset forum password."
msgstr "Forum Passwort zurücksetzen."
#: allianceauth/services/modules/phpbb3/views.py:100
msgid "Set forum password."
msgstr "Setze Forum Passwort."
#: allianceauth/services/modules/smf/views.py:34
msgid "Activated SMF account."
msgstr "SMF Konto aktiviert."
#: allianceauth/services/modules/smf/views.py:43
#: allianceauth/services/modules/smf/views.py:58
#: allianceauth/services/modules/smf/views.py:80
#: allianceauth/services/modules/smf/views.py:103
msgid "An error occurred while processing your SMF account."
msgstr "Es gab einen Fehler bei der Verarbeitung Deines SMF Kontos."
#: allianceauth/services/modules/smf/views.py:55
msgid "Deactivated SMF account."
msgstr "SMF Konto deaktiviert."
#: allianceauth/services/modules/smf/views.py:72
msgid "Reset SMF password."
msgstr "SMF Passwort zurücksetzen."
#: allianceauth/services/modules/smf/views.py:100
msgid "Set SMF password."
msgstr "Setze SMF Passwort."
#: allianceauth/services/modules/teamspeak3/forms.py:14
#, python-format
msgid "Unable to locate user %s on server"
@@ -1300,6 +1480,47 @@ msgstr "Server beitreten"
msgid "Continue"
msgstr "Fortsetzen"
#: allianceauth/services/modules/teamspeak3/views.py:34
msgid "Activated TeamSpeak3 account."
msgstr "TeamSpeak3 Konto aktiviert."
#: allianceauth/services/modules/teamspeak3/views.py:37
#: allianceauth/services/modules/teamspeak3/views.py:74
#: allianceauth/services/modules/teamspeak3/views.py:100
msgid "An error occurred while processing your TeamSpeak3 account."
msgstr "Es gab einen Fehler bei der Verarbeitung Deines TeamSpeak3 Kontos."
#: allianceauth/services/modules/teamspeak3/views.py:71
msgid "Deactivated TeamSpeak3 account."
msgstr "TeamSpeak3 Konto deaktiviert."
#: allianceauth/services/modules/teamspeak3/views.py:97
msgid "Reset TeamSpeak3 permission key."
msgstr "TeamSpeak3 Berechtigungsschlüssel zurücksetzen."
#: allianceauth/services/modules/xenforo/views.py:30
msgid "Activated XenForo account."
msgstr "XenForo Konto aktiviert."
#: allianceauth/services/modules/xenforo/views.py:40
#: allianceauth/services/modules/xenforo/views.py:52
#: allianceauth/services/modules/xenforo/views.py:73
#: allianceauth/services/modules/xenforo/views.py:94
msgid "An error occurred while processing your XenForo account."
msgstr "Es gab einen Fehler bei der Verarbeitung Deines XenForo Kontos."
#: allianceauth/services/modules/xenforo/views.py:50
msgid "Deactivated XenForo account."
msgstr "XenForo Konto deaktiviert."
#: allianceauth/services/modules/xenforo/views.py:65
msgid "Reset XenForo account password."
msgstr "XenForo Passwort zurüclsetzen."
#: allianceauth/services/modules/xenforo/views.py:91
msgid "Changed XenForo password."
msgstr "Setze XenForo Passwort."
#: allianceauth/services/templates/services/fleetformattertool.html:6
msgid "Fleet Formatter Tool"
msgstr "Flottenformatierung"

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-09 15:16+0000\n"
"POT-Creation-Date: 2020-04-02 03:23+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -65,9 +65,7 @@ msgid "Change Main"
msgstr ""
#: allianceauth/authentication/templates/authentication/dashboard.html:97
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgid "Group Memberships"
msgstr ""
#: allianceauth/authentication/templates/authentication/dashboard.html:117
@@ -132,40 +130,47 @@ msgstr ""
msgid "Submit"
msgstr ""
#: allianceauth/authentication/views.py:39
#: allianceauth/authentication/views.py:74
#, python-format
msgid ""
"Cannot change main character to %(char)s: character owned by a different "
"account."
msgstr ""
#: allianceauth/authentication/views.py:80
#, python-format
msgid "Changed main character to %(char)s"
msgstr ""
#: allianceauth/authentication/views.py:48
#: allianceauth/authentication/views.py:89
#, python-format
msgid "Added %(name)s to your account."
msgstr ""
#: allianceauth/authentication/views.py:50
#: allianceauth/authentication/views.py:91
#, python-format
msgid "Failed to add %(name)s to your account: they already have an account."
msgstr ""
#: allianceauth/authentication/views.py:89
#: allianceauth/authentication/views.py:130
msgid "Unable to authenticate as the selected character."
msgstr ""
#: allianceauth/authentication/views.py:107
#: allianceauth/authentication/views.py:148
msgid "Registration token has expired."
msgstr ""
#: allianceauth/authentication/views.py:159
#: allianceauth/authentication/views.py:200
msgid ""
"Sent confirmation email. Please follow the link to confirm your email "
"address."
msgstr ""
#: allianceauth/authentication/views.py:164
#: allianceauth/authentication/views.py:205
msgid "Confirmed your email address. Please login to continue."
msgstr ""
#: allianceauth/authentication/views.py:169
#: allianceauth/authentication/views.py:210
msgid "Registraion of new accounts it not allowed at this time."
msgstr ""
@@ -527,6 +532,12 @@ msgstr ""
msgid "Audit Log"
msgstr ""
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:18
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:20
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:13
msgid "Back"
msgstr ""
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:25
msgid "Date/Time"
msgstr ""
@@ -586,6 +597,12 @@ msgstr ""
msgid "Groups Membership"
msgstr ""
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:14
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgstr ""
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:23
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
msgid "Description"
@@ -705,61 +722,70 @@ msgstr ""
msgid "Group Membership"
msgstr ""
#: allianceauth/groupmanagement/views.py:164
#: allianceauth/groupmanagement/views.py:166
#, python-format
msgid "Removed user %(user)s from group %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:166
#: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group"
msgstr ""
#: allianceauth/groupmanagement/views.py:169
#: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist"
msgstr ""
#: allianceauth/groupmanagement/views.py:196
#: allianceauth/groupmanagement/views.py:198
#, python-format
msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:203
#: allianceauth/groupmanagement/views.py:236
#: allianceauth/groupmanagement/views.py:205
#: allianceauth/groupmanagement/views.py:238
#, python-format
msgid ""
"An unhandled error occurred while processing the application from "
"%(mainchar)s to %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:229
#: allianceauth/groupmanagement/views.py:231
#, python-format
msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:265
#: allianceauth/groupmanagement/views.py:267
#, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:271
#: allianceauth/groupmanagement/views.py:305
#: allianceauth/groupmanagement/views.py:273
#: allianceauth/groupmanagement/views.py:307
#, python-format
msgid ""
"An unhandled error occured while processing the application from "
"An unhandled error occurred while processing the application from "
"%(mainchar)s to leave %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:298
#: allianceauth/groupmanagement/views.py:300
#, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:345
#: allianceauth/groupmanagement/views.py:346
#: allianceauth/groupmanagement/views.py:358
msgid "You cannot join that group"
msgstr ""
#: allianceauth/groupmanagement/views.py:369
#: allianceauth/groupmanagement/views.py:407
#: allianceauth/groupmanagement/views.py:352
msgid "You are already a member of that group."
msgstr ""
#: allianceauth/groupmanagement/views.py:367
msgid "You already have a pending application for that group."
msgstr ""
#: allianceauth/groupmanagement/views.py:370
#: allianceauth/groupmanagement/views.py:408
#: allianceauth/hrapplications/templates/hrapplications/management.html:37
#: allianceauth/hrapplications/templates/hrapplications/management.html:72
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
@@ -771,20 +797,24 @@ msgstr ""
msgid "Pending"
msgstr ""
#: allianceauth/groupmanagement/views.py:375
#: allianceauth/groupmanagement/views.py:376
#, python-format
msgid "Applied to group %(group)s."
msgstr ""
#: allianceauth/groupmanagement/views.py:386
#: allianceauth/groupmanagement/views.py:387
msgid "You cannot leave that group"
msgstr ""
#: allianceauth/groupmanagement/views.py:391
#: allianceauth/groupmanagement/views.py:392
msgid "You are not a member of that group"
msgstr ""
#: allianceauth/groupmanagement/views.py:413
#: allianceauth/groupmanagement/views.py:401
msgid "You already have a pending leave request for that group."
msgstr ""
#: allianceauth/groupmanagement/views.py:414
#, python-format
msgid "Applied to leave group %(group)s."
msgstr ""
@@ -1130,10 +1160,6 @@ msgstr ""
msgid "Permissions Audit"
msgstr ""
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:13
msgid "Back"
msgstr ""
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:22
msgid "User / Character"
msgstr ""
@@ -1179,6 +1205,18 @@ msgstr ""
msgid "States"
msgstr ""
#: allianceauth/services/abstract.py:72
msgid "That service account already exists"
msgstr ""
#: allianceauth/services/abstract.py:104
msgid "Successfully set your {} password"
msgstr ""
#: allianceauth/services/auth_hooks.py:11
msgid "Services"
msgstr ""
#: allianceauth/services/forms.py:6
msgid "Name of Fleet:"
msgstr ""
@@ -1243,15 +1281,79 @@ msgstr ""
msgid "Link Discord Server"
msgstr ""
#: allianceauth/services/modules/openfire/forms.py:7
msgid "Message"
#: allianceauth/services/modules/discord/views.py:26
msgid "Deactivated Discord account."
msgstr ""
#: allianceauth/services/modules/discord/views.py:29
#: allianceauth/services/modules/discord/views.py:41
#: allianceauth/services/modules/discord/views.py:65
msgid "An error occurred while processing your Discord account."
msgstr ""
#: allianceauth/services/modules/discord/views.py:62
msgid "Activated Discord account."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:37
msgid "You are not authorized to access Discourse."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:42
msgid "You must have a main character set to access Discourse."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:52
msgid ""
"No SSO payload or signature. Please contact support if this problem persists."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:62
#: allianceauth/services/modules/discourse/views.py:70
msgid "Invalid payload. Please contact support if this problem persists."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:31
msgid "Activated IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:40
#: allianceauth/services/modules/ips4/views.py:62
#: allianceauth/services/modules/ips4/views.py:83
#: allianceauth/services/modules/ips4/views.py:103
msgid "An error occurred while processing your IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:53
msgid "Reset IPSuite4 password."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:80
msgid "Set IPSuite4 password."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:100
msgid "Deactivated IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/openfire/auth_hooks.py:26
msgid "Jabber"
msgstr ""
#: allianceauth/services/modules/openfire/auth_hooks.py:78
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:6
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:11
msgid "Jabber Broadcast"
msgstr ""
#: allianceauth/services/modules/openfire/auth_hooks.py:91
msgid "Fleet Broadcast Formatter"
msgstr ""
#: allianceauth/services/modules/openfire/forms.py:7
msgid "Message"
msgstr ""
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:17
msgid "Broadcast Sent!!"
msgstr ""
@@ -1260,6 +1362,76 @@ msgstr ""
msgid "Broadcast"
msgstr ""
#: allianceauth/services/modules/openfire/views.py:35
msgid "Activated jabber account."
msgstr ""
#: allianceauth/services/modules/openfire/views.py:44
#: allianceauth/services/modules/openfire/views.py:57
#: allianceauth/services/modules/openfire/views.py:78
#: allianceauth/services/modules/openfire/views.py:151
msgid "An error occurred while processing your jabber account."
msgstr ""
#: allianceauth/services/modules/openfire/views.py:70
msgid "Reset jabber password."
msgstr ""
#: allianceauth/services/modules/openfire/views.py:119
#, python-format
msgid "Sent jabber broadcast to %s"
msgstr ""
#: allianceauth/services/modules/openfire/views.py:148
msgid "Set jabber password."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:34
msgid "Activated forum account."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:43
#: allianceauth/services/modules/phpbb3/views.py:57
#: allianceauth/services/modules/phpbb3/views.py:80
#: allianceauth/services/modules/phpbb3/views.py:103
msgid "An error occurred while processing your forum account."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:54
msgid "Deactivated forum account."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:71
msgid "Reset forum password."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:100
msgid "Set forum password."
msgstr ""
#: allianceauth/services/modules/smf/views.py:34
msgid "Activated SMF account."
msgstr ""
#: allianceauth/services/modules/smf/views.py:43
#: allianceauth/services/modules/smf/views.py:58
#: allianceauth/services/modules/smf/views.py:80
#: allianceauth/services/modules/smf/views.py:103
msgid "An error occurred while processing your SMF account."
msgstr ""
#: allianceauth/services/modules/smf/views.py:55
msgid "Deactivated SMF account."
msgstr ""
#: allianceauth/services/modules/smf/views.py:72
msgid "Reset SMF password."
msgstr ""
#: allianceauth/services/modules/smf/views.py:100
msgid "Set SMF password."
msgstr ""
#: allianceauth/services/modules/teamspeak3/forms.py:14
#, python-format
msgid "Unable to locate user %s on server"
@@ -1283,6 +1455,47 @@ msgstr ""
msgid "Continue"
msgstr ""
#: allianceauth/services/modules/teamspeak3/views.py:34
msgid "Activated TeamSpeak3 account."
msgstr ""
#: allianceauth/services/modules/teamspeak3/views.py:37
#: allianceauth/services/modules/teamspeak3/views.py:74
#: allianceauth/services/modules/teamspeak3/views.py:100
msgid "An error occurred while processing your TeamSpeak3 account."
msgstr ""
#: allianceauth/services/modules/teamspeak3/views.py:71
msgid "Deactivated TeamSpeak3 account."
msgstr ""
#: allianceauth/services/modules/teamspeak3/views.py:97
msgid "Reset TeamSpeak3 permission key."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:30
msgid "Activated XenForo account."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:40
#: allianceauth/services/modules/xenforo/views.py:52
#: allianceauth/services/modules/xenforo/views.py:73
#: allianceauth/services/modules/xenforo/views.py:94
msgid "An error occurred while processing your XenForo account."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:50
msgid "Deactivated XenForo account."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:65
msgid "Reset XenForo account password."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:91
msgid "Changed XenForo password."
msgstr ""
#: allianceauth/services/templates/services/fleetformattertool.html:6
msgid "Fleet Formatter Tool"
msgstr ""

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-04 23:04+0000\n"
"POT-Creation-Date: 2020-03-26 03:07+0000\n"
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
"Last-Translator: frank1210 <francolopez_16@hotmail.com>, 2020\n"
"Language-Team: Spanish (https://www.transifex.com/alliance-auth/teams/107430/es/)\n"
@@ -70,10 +70,8 @@ msgid "Change Main"
msgstr "Cambiar Personaje Principal"
#: allianceauth/authentication/templates/authentication/dashboard.html:97
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgstr "Grupos"
msgid "Group Memberships"
msgstr ""
#: allianceauth/authentication/templates/authentication/dashboard.html:117
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
@@ -137,45 +135,52 @@ msgstr "Tu equipo de IT"
msgid "Submit"
msgstr "Enviar"
#: allianceauth/authentication/views.py:39
#: allianceauth/authentication/views.py:74
#, python-format
msgid ""
"Cannot change main character to %(char)s: character owned by a different "
"account."
msgstr ""
#: allianceauth/authentication/views.py:80
#, python-format
msgid "Changed main character to %(char)s"
msgstr "Se ha cambiado tu personaje principal ha %(char)s"
#: allianceauth/authentication/views.py:48
#: allianceauth/authentication/views.py:89
#, python-format
msgid "Added %(name)s to your account."
msgstr "Se ha agregado a %(name)s a tu cuenta"
#: allianceauth/authentication/views.py:50
#: allianceauth/authentication/views.py:91
#, 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:89
#: allianceauth/authentication/views.py:130
msgid "Unable to authenticate as the selected character."
msgstr "Imposible validar con el personaje seleccionado."
#: allianceauth/authentication/views.py:107
#: allianceauth/authentication/views.py:148
msgid "Registration token has expired."
msgstr "El token de registracion expiro."
#: allianceauth/authentication/views.py:159
#: allianceauth/authentication/views.py:200
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:164
#: allianceauth/authentication/views.py:205
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:169
#: allianceauth/authentication/views.py:210
msgid "Registraion of new accounts it not allowed at this time."
msgstr "Registracion de nuevas cuentas no es permitido por el momento."
@@ -538,6 +543,12 @@ msgstr "Enlace de participacion expirado."
msgid "Audit Log"
msgstr "Log de Auditoria"
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:18
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:20
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:13
msgid "Back"
msgstr "Volver"
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:25
msgid "Date/Time"
msgstr "Fecha/Hora"
@@ -597,6 +608,12 @@ msgstr "no hay miembros para listar."
msgid "Groups Membership"
msgstr "Membresia de grupos"
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:14
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgstr "Grupos"
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:23
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:16
msgid "Description"
@@ -716,26 +733,26 @@ msgstr "Solicitudes de Grupo"
msgid "Group Membership"
msgstr "Membresia de Grupo"
#: allianceauth/groupmanagement/views.py:164
#: 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:166
#: 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:169
#: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist"
msgstr "El grupo no existe"
#: allianceauth/groupmanagement/views.py:196
#: 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:203
#: allianceauth/groupmanagement/views.py:236
#: allianceauth/groupmanagement/views.py:205
#: allianceauth/groupmanagement/views.py:238
#, python-format
msgid ""
"An unhandled error occurred while processing the application from "
@@ -744,38 +761,45 @@ msgstr ""
"Ocurrio un error cuando se intento procesar la informacion de %(mainchar)s "
"al grupo %(group)s."
#: allianceauth/groupmanagement/views.py:229
#: allianceauth/groupmanagement/views.py:231
#, 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:265
#: allianceauth/groupmanagement/views.py:267
#, 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:271
#: allianceauth/groupmanagement/views.py:305
#: allianceauth/groupmanagement/views.py:273
#: allianceauth/groupmanagement/views.py:307
#, python-format
msgid ""
"An unhandled error occured while processing the application from "
"An unhandled error occurred while processing the application from "
"%(mainchar)s to leave %(group)s."
msgstr ""
"Ocurrio un error cuando se intento procesar la informacion de %(mainchar)s "
"para dejar el grupo %(group)s."
#: allianceauth/groupmanagement/views.py:298
#: allianceauth/groupmanagement/views.py:300
#, 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:345
#: allianceauth/groupmanagement/views.py:347
#: allianceauth/groupmanagement/views.py:359
msgid "You cannot join that group"
msgstr "No puedes unirte a ese grupo"
#: allianceauth/groupmanagement/views.py:369
#: allianceauth/groupmanagement/views.py:407
#: allianceauth/groupmanagement/views.py:353
msgid "You are already a member of that group."
msgstr ""
#: allianceauth/groupmanagement/views.py:368
msgid "You already have a pending application for that group."
msgstr ""
#: allianceauth/groupmanagement/views.py:371
#: allianceauth/groupmanagement/views.py:409
#: allianceauth/hrapplications/templates/hrapplications/management.html:37
#: allianceauth/hrapplications/templates/hrapplications/management.html:72
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
@@ -787,20 +811,24 @@ msgstr "No puedes unirte a ese grupo"
msgid "Pending"
msgstr "Pendiente"
#: allianceauth/groupmanagement/views.py:375
#: allianceauth/groupmanagement/views.py:377
#, python-format
msgid "Applied to group %(group)s."
msgstr "Solicitud enviada al grupo %(group)s."
#: allianceauth/groupmanagement/views.py:386
#: allianceauth/groupmanagement/views.py:388
msgid "You cannot leave that group"
msgstr "No puedes dejar el grupos"
#: allianceauth/groupmanagement/views.py:391
#: allianceauth/groupmanagement/views.py:393
msgid "You are not a member of that group"
msgstr "No eres miembro de ese grupo"
#: allianceauth/groupmanagement/views.py:413
#: allianceauth/groupmanagement/views.py:402
msgid "You already have a pending leave request for that group."
msgstr ""
#: allianceauth/groupmanagement/views.py:415
#, python-format
msgid "Applied to leave group %(group)s."
msgstr "Solicitaste dejar el grupo %(group)s."
@@ -1146,10 +1174,6 @@ msgstr "Se guardaron los cambios para la operacion %(opname)s"
msgid "Permissions Audit"
msgstr "Auditar Permisos"
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:13
msgid "Back"
msgstr "Volver"
#: allianceauth/permissions_tool/templates/permissions_tool/audit.html:22
msgid "User / Character"
msgstr ""
@@ -1195,6 +1219,18 @@ msgstr "Usuarios"
msgid "States"
msgstr "Estados"
#: allianceauth/services/abstract.py:72
msgid "That service account already exists"
msgstr ""
#: allianceauth/services/abstract.py:104
msgid "Successfully set your {} password"
msgstr ""
#: allianceauth/services/auth_hooks.py:11
msgid "Services"
msgstr ""
#: allianceauth/services/forms.py:6
msgid "Name of Fleet:"
msgstr "Nombre de la flota:"
@@ -1259,15 +1295,80 @@ msgstr "La contraseña tiene que tener 8 caracteres de largo minimo"
msgid "Link Discord Server"
msgstr "Enlace a servidor de Discord"
#: allianceauth/services/modules/openfire/forms.py:7
msgid "Message"
msgstr "Mensaje"
#: allianceauth/services/modules/discord/views.py:26
msgid "Deactivated Discord account."
msgstr ""
#: allianceauth/services/modules/discord/views.py:29
#: allianceauth/services/modules/discord/views.py:41
#: allianceauth/services/modules/discord/views.py:65
msgid "An error occurred while processing your Discord account."
msgstr ""
#: allianceauth/services/modules/discord/views.py:62
msgid "Activated Discord account."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:37
msgid "You are not authorized to access Discourse."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:42
msgid "You must have a main character set to access Discourse."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:52
msgid ""
"No SSO payload or signature. Please contact support if this problem "
"persists."
msgstr ""
#: allianceauth/services/modules/discourse/views.py:62
#: allianceauth/services/modules/discourse/views.py:70
msgid "Invalid payload. Please contact support if this problem persists."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:31
msgid "Activated IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:40
#: allianceauth/services/modules/ips4/views.py:62
#: allianceauth/services/modules/ips4/views.py:83
#: allianceauth/services/modules/ips4/views.py:103
msgid "An error occurred while processing your IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:53
msgid "Reset IPSuite4 password."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:80
msgid "Set IPSuite4 password."
msgstr ""
#: allianceauth/services/modules/ips4/views.py:100
msgid "Deactivated IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/openfire/auth_hooks.py:26
msgid "Jabber"
msgstr ""
#: allianceauth/services/modules/openfire/auth_hooks.py:78
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:6
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:11
msgid "Jabber Broadcast"
msgstr "Anuncio por Jabber"
#: allianceauth/services/modules/openfire/auth_hooks.py:91
msgid "Fleet Broadcast Formatter"
msgstr ""
#: allianceauth/services/modules/openfire/forms.py:7
msgid "Message"
msgstr "Mensaje"
#: allianceauth/services/modules/openfire/templates/services/openfire/broadcast.html:17
msgid "Broadcast Sent!!"
msgstr "Ping Enviado"
@@ -1276,6 +1377,76 @@ msgstr "Ping Enviado"
msgid "Broadcast"
msgstr "Anuncio"
#: allianceauth/services/modules/openfire/views.py:35
msgid "Activated jabber account."
msgstr ""
#: allianceauth/services/modules/openfire/views.py:44
#: allianceauth/services/modules/openfire/views.py:57
#: allianceauth/services/modules/openfire/views.py:78
#: allianceauth/services/modules/openfire/views.py:151
msgid "An error occurred while processing your jabber account."
msgstr ""
#: allianceauth/services/modules/openfire/views.py:70
msgid "Reset jabber password."
msgstr ""
#: allianceauth/services/modules/openfire/views.py:119
#, python-format
msgid "Sent jabber broadcast to %s"
msgstr ""
#: allianceauth/services/modules/openfire/views.py:148
msgid "Set jabber password."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:34
msgid "Activated forum account."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:43
#: allianceauth/services/modules/phpbb3/views.py:57
#: allianceauth/services/modules/phpbb3/views.py:80
#: allianceauth/services/modules/phpbb3/views.py:103
msgid "An error occurred while processing your forum account."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:54
msgid "Deactivated forum account."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:71
msgid "Reset forum password."
msgstr ""
#: allianceauth/services/modules/phpbb3/views.py:100
msgid "Set forum password."
msgstr ""
#: allianceauth/services/modules/smf/views.py:34
msgid "Activated SMF account."
msgstr ""
#: allianceauth/services/modules/smf/views.py:43
#: allianceauth/services/modules/smf/views.py:58
#: allianceauth/services/modules/smf/views.py:80
#: allianceauth/services/modules/smf/views.py:103
msgid "An error occurred while processing your SMF account."
msgstr ""
#: allianceauth/services/modules/smf/views.py:55
msgid "Deactivated SMF account."
msgstr ""
#: allianceauth/services/modules/smf/views.py:72
msgid "Reset SMF password."
msgstr ""
#: allianceauth/services/modules/smf/views.py:100
msgid "Set SMF password."
msgstr ""
#: allianceauth/services/modules/teamspeak3/forms.py:14
#, python-format
msgid "Unable to locate user %s on server"
@@ -1299,6 +1470,47 @@ msgstr "Unirse al Servidor"
msgid "Continue"
msgstr "Continuar"
#: allianceauth/services/modules/teamspeak3/views.py:34
msgid "Activated TeamSpeak3 account."
msgstr ""
#: allianceauth/services/modules/teamspeak3/views.py:37
#: allianceauth/services/modules/teamspeak3/views.py:74
#: allianceauth/services/modules/teamspeak3/views.py:100
msgid "An error occurred while processing your TeamSpeak3 account."
msgstr ""
#: allianceauth/services/modules/teamspeak3/views.py:71
msgid "Deactivated TeamSpeak3 account."
msgstr ""
#: allianceauth/services/modules/teamspeak3/views.py:97
msgid "Reset TeamSpeak3 permission key."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:30
msgid "Activated XenForo account."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:40
#: allianceauth/services/modules/xenforo/views.py:52
#: allianceauth/services/modules/xenforo/views.py:73
#: allianceauth/services/modules/xenforo/views.py:94
msgid "An error occurred while processing your XenForo account."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:50
msgid "Deactivated XenForo account."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:65
msgid "Reset XenForo account password."
msgstr ""
#: allianceauth/services/modules/xenforo/views.py:91
msgid "Changed XenForo password."
msgstr ""
#: allianceauth/services/templates/services/fleetformattertool.html:6
msgid "Fleet Formatter Tool"
msgstr "Formato de Ping"

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -5,15 +5,16 @@
#
# Translators:
# Joel Falknau <ozirascal@gmail.com>, 2020
# Jesse . <sgeine@hotmail.com>, 2020
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-04 23:04+0000\n"
"POT-Creation-Date: 2020-03-10 01:32+0000\n"
"PO-Revision-Date: 2020-02-18 03:14+0000\n"
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2020\n"
"Last-Translator: Jesse . <sgeine@hotmail.com>, 2020\n"
"Language-Team: Chinese Simplified (https://www.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -68,10 +69,8 @@ msgid "Change Main"
msgstr "修改主要角色"
#: allianceauth/authentication/templates/authentication/dashboard.html:97
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgstr "群组"
msgid "Group Memberships"
msgstr "用户组成员"
#: allianceauth/authentication/templates/authentication/dashboard.html:117
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:23
@@ -135,40 +134,40 @@ msgstr "您的IT团队"
msgid "Submit"
msgstr "提交"
#: allianceauth/authentication/views.py:39
#: allianceauth/authentication/views.py:77
#, python-format
msgid "Changed main character to %(char)s"
msgstr "修改主要角色为%(char)s"
#: allianceauth/authentication/views.py:48
#: allianceauth/authentication/views.py:86
#, python-format
msgid "Added %(name)s to your account."
msgstr "添加%(name)s到您的账户"
#: allianceauth/authentication/views.py:50
#: allianceauth/authentication/views.py:88
#, python-format
msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "添加%(name)s到您的账户失败他们已经在一个账户中了"
#: allianceauth/authentication/views.py:89
#: allianceauth/authentication/views.py:127
msgid "Unable to authenticate as the selected character."
msgstr "无法作为选定的角色进行身份验证"
#: allianceauth/authentication/views.py:107
#: allianceauth/authentication/views.py:145
msgid "Registration token has expired."
msgstr "注册令牌过期。"
#: allianceauth/authentication/views.py:159
#: allianceauth/authentication/views.py:197
msgid ""
"Sent confirmation email. Please follow the link to confirm your email "
"address."
msgstr "已经发送了确认邮件。请按照链接确定您的电邮地址"
#: allianceauth/authentication/views.py:164
#: allianceauth/authentication/views.py:202
msgid "Confirmed your email address. Please login to continue."
msgstr "已确认您的电邮地址。请登录以继续"
#: allianceauth/authentication/views.py:169
#: allianceauth/authentication/views.py:207
msgid "Registraion of new accounts it not allowed at this time."
msgstr "现在不允许注册新账户。"
@@ -706,61 +705,61 @@ msgstr "用户组请求"
msgid "Group Membership"
msgstr "用户组成员"
#: allianceauth/groupmanagement/views.py:164
#: allianceauth/groupmanagement/views.py:165
#, python-format
msgid "Removed user %(user)s from group %(group)s."
msgstr "已将用户%(user)s从用户组%(group)s中移除"
#: allianceauth/groupmanagement/views.py:166
#: allianceauth/groupmanagement/views.py:167
msgid "User does not exist in that group"
msgstr "那个用户组中不存在这个用户"
#: allianceauth/groupmanagement/views.py:169
#: allianceauth/groupmanagement/views.py:170
msgid "Group does not exist"
msgstr "用户组不存在"
#: allianceauth/groupmanagement/views.py:196
#: allianceauth/groupmanagement/views.py:197
#, python-format
msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "已接受用户%(mainchar)s加入%(group)s的申请"
#: allianceauth/groupmanagement/views.py:203
#: allianceauth/groupmanagement/views.py:236
#: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:237
#, 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:229
#: allianceauth/groupmanagement/views.py:230
#, python-format
msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "%(mainchar)s加入%(group)s的申请已拒绝"
#: allianceauth/groupmanagement/views.py:265
#: allianceauth/groupmanagement/views.py:266
#, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)s加入%(group)s的申请已通过"
#: allianceauth/groupmanagement/views.py:271
#: allianceauth/groupmanagement/views.py:305
#: allianceauth/groupmanagement/views.py:272
#: allianceauth/groupmanagement/views.py:306
#, python-format
msgid ""
"An unhandled error occured while processing the application from "
"%(mainchar)s to leave %(group)s."
msgstr "在处理%(mainchar)s离开%(group)s的请求时发生了搞不定的错误"
#: allianceauth/groupmanagement/views.py:298
#: allianceauth/groupmanagement/views.py:299
#, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)s离开%(group)s的请求已被拒绝"
#: allianceauth/groupmanagement/views.py:345
#: allianceauth/groupmanagement/views.py:346
msgid "You cannot join that group"
msgstr "你无法加入那个用户组"
#: allianceauth/groupmanagement/views.py:369
#: allianceauth/groupmanagement/views.py:407
#: allianceauth/groupmanagement/views.py:370
#: allianceauth/groupmanagement/views.py:408
#: allianceauth/hrapplications/templates/hrapplications/management.html:37
#: allianceauth/hrapplications/templates/hrapplications/management.html:72
#: allianceauth/hrapplications/templates/hrapplications/management.html:99
@@ -772,20 +771,20 @@ msgstr "你无法加入那个用户组"
msgid "Pending"
msgstr "待定"
#: allianceauth/groupmanagement/views.py:375
#: allianceauth/groupmanagement/views.py:376
#, python-format
msgid "Applied to group %(group)s."
msgstr "修改已经应用到%(group)s啦"
#: allianceauth/groupmanagement/views.py:386
#: allianceauth/groupmanagement/views.py:387
msgid "You cannot leave that group"
msgstr "你无法离开那个用户组"
#: allianceauth/groupmanagement/views.py:391
#: allianceauth/groupmanagement/views.py:392
msgid "You are not a member of that group"
msgstr "你不是那个用户组的成员"
#: allianceauth/groupmanagement/views.py:413
#: allianceauth/groupmanagement/views.py:414
#, python-format
msgid "Applied to leave group %(group)s."
msgstr "已经离开群组%(group)s"
@@ -1176,6 +1175,11 @@ msgstr "操作类型"
msgid "Users"
msgstr "用户"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:40
#: allianceauth/templates/allianceauth/side-menu.html:15
msgid "Groups"
msgstr "群组"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:43
msgid "States"
msgstr "声望"

View File

@@ -11,6 +11,15 @@ app = Celery('{{ project_name }}')
# Using a string here means the worker don't have to serialize
# the configuration object to child processes.
app.config_from_object('django.conf:settings')
# setup priorities ( 0 Highest, 9 Lowest )
app.conf.broker_transport_options = {
'priority_steps': list(range(10)), # setup que to have 10 steps
'queue_order_strategy': 'priority', # setup que to use prio sorting
}
app.conf.task_default_priority = 5 # anything called with the task.delay() will be given normal priority (5)
app.conf.worker_prefetch_multiplier = 1 # only prefetch single tasks at a time on the workers so that prio tasks happen
app.conf.ONCE = {
'backend': 'allianceauth.services.tasks.DjangoBackend',
'settings': {}

View File

@@ -84,6 +84,8 @@ LANGUAGES = (
('de', ugettext('German')),
('es', ugettext('Spanish')),
('zh-hans', ugettext('Chinese Simplified')),
('ru', ugettext('Russian')),
('ko', ugettext('Korean')),
)
TEMPLATES = [
@@ -218,6 +220,14 @@ LOGGING = {
'maxBytes': 1024 * 1024 * 5, # edit this line to change max log file size
'backupCount': 5, # edit this line to change number of log backups
},
'extension_file': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'log/extensions.log'),
'formatter': 'verbose',
'maxBytes': 1024 * 1024 * 5, # edit this line to change max log file size
'backupCount': 5, # edit this line to change number of log backups
},
'console': {
'level': 'DEBUG', # edit this line to change logging level to console
'class': 'logging.StreamHandler',
@@ -234,6 +244,10 @@ LOGGING = {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'extensions': {
'handlers': ['extension_file', 'console'],
'level': 'DEBUG',
},
'django': {
'handlers': ['log_file', 'console'],
'level': 'ERROR',

View File

@@ -17,6 +17,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix
from django.db import models, IntegrityError
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import render, Http404, redirect
from django.utils.translation import gettext_lazy as _
from .forms import ServicePasswordModelForm
@@ -68,7 +69,7 @@ class BaseCreatePasswordServiceAccountView(BaseServiceView, ServiceCredentialsVi
try:
svc_obj = self.model.objects.create(user=request.user)
except IntegrityError:
messages.error(request, "That service account already exists")
messages.error(request, _("That service account already exists"))
return redirect(self.index_redirect)
return render(request, self.template_name,
@@ -100,7 +101,7 @@ class BaseSetPasswordServiceAccountView(ServicesCRUDMixin, BaseServiceView, Upda
def post(self, request, *args, **kwargs):
result = super(BaseSetPasswordServiceAccountView, self).post(request, *args, **kwargs)
if self.get_form().is_valid():
messages.success(request, "Successfully set your {} password".format(self.service_name))
messages.success(request, _("Successfully set your {} password".format(self.service_name)))
return result

View File

@@ -1,4 +1,6 @@
from django.utils.translation import gettext_lazy as _
from allianceauth import hooks
from .hooks import MenuItemHook
from .hooks import ServicesHook
@@ -6,7 +8,7 @@ from .hooks import ServicesHook
class Services(MenuItemHook):
def __init__(self):
MenuItemHook.__init__(self,
'Services',
_('Services'),
'fa fa-cogs fa-fw',
'services:services', 100)

View File

@@ -9,6 +9,29 @@ from allianceauth.hooks import get_hooks
from .models import NameFormatConfig
def get_extension_logger(name):
"""
Takes the name of a plugin/extension and generates a child logger of the extensions logger
to be used by the extension to log events to the extensions logger.
The logging level is decided by whether or not DEBUG is set to true in the project settings. If
DEBUG is set to false, then the logging level is set to INFO.
:param: name: the name of the extension doing the logging
:return: an extensions child logger
"""
import logging
from django.conf import settings
logger = logging.getLogger('extensions.' + name)
logger.name = name
logger.level = logging.INFO
if settings.DEBUG:
logger.level = logging.DEBUG
return logger
class ServicesHook:
"""
Abstract base class for creating a compatible services

View File

@@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-09-05 21:40
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0008_alter_user_username_max_length'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='DiscordAuthToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.CharField(max_length=254, unique=True)),
('token', models.CharField(max_length=254)),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='GroupCache',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('groups', models.TextField(default={})),
('service', models.CharField(choices=[(b'discourse', b'discourse'), (b'discord', b'discord')], max_length=254, unique=True)),
],
),
]

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-10-16 01:35
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('services', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='discordauthtoken',
name='user',
),
migrations.DeleteModel(
name='DiscordAuthToken',
),
]

View File

@@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-09-02 06:07
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('services', '0002_auto_20161016_0135'),
]
operations = [
migrations.DeleteModel(
name='GroupCache',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.2.10 on 2020-03-21 13:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('services', '0002_nameformatter'),
]
operations = [
migrations.AlterField(
model_name='nameformatconfig',
name='format',
field=models.CharField(help_text='For information on constructing name formats please see the official documentation, topic "Services Name Formats".', max_length=100),
),
]

View File

@@ -4,14 +4,30 @@ from allianceauth.authentication.models import State
class NameFormatConfig(models.Model):
service_name = models.CharField(max_length=100, blank=False, null=False)
default_to_username = models.BooleanField(default=True, help_text="If a user has no main_character, "
"default to using their Auth username instead.")
format = models.CharField(max_length=100, blank=False, null=False,
help_text='For information on constructing name formats, please see the '
'<a href="https://allianceauth.readthedocs.io/en/latest/features/nameformats">'
'name format documentation</a>')
states = models.ManyToManyField(State, help_text="States to apply this format to. You should only have one "
"formatter for each state for each service.")
service_name = models.CharField(max_length=100, blank=False)
default_to_username = models.BooleanField(
default=True,
help_text=
'If a user has no main_character, '
'default to using their Auth username instead.'
)
format = models.CharField(
max_length=100,
blank=False,
help_text=
'For information on constructing name formats '
'please see the official documentation, '
'topic "Services Name Formats".'
)
states = models.ManyToManyField(
State,
help_text=
"States to apply this format to. You should only have one "
"formatter for each state for each service."
)
def __str__(self):
return '%s: %s' % (
self.service_name, ', '.join([str(x) for x in self.states.all()])
)

View File

@@ -5,8 +5,10 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import redirect
from django.utils.translation import gettext_lazy as _
from allianceauth.services.views import superuser_test
from .manager import DiscordOAuthManager
from .tasks import DiscordTasks
@@ -21,10 +23,10 @@ def deactivate_discord(request):
logger.debug("deactivate_discord called by user %s" % request.user)
if DiscordTasks.delete_user(request.user):
logger.info("Successfully deactivated discord for user %s" % request.user)
messages.success(request, 'Deactivated Discord account.')
messages.success(request, _('Deactivated Discord account.'))
else:
logger.error("Unsuccessful attempt to deactivate discord for user %s" % request.user)
messages.error(request, 'An error occurred while processing your Discord account.')
messages.error(request, _('An error occurred while processing your Discord account.'))
return redirect("services:services")
@@ -36,7 +38,7 @@ def reset_discord(request):
logger.info("Successfully deleted discord user for user %s - forwarding to discord activation." % request.user)
return redirect("discord:activate")
logger.error("Unsuccessful attempt to reset discord for user %s" % request.user)
messages.error(request, 'An error occurred while processing your Discord account.')
messages.error(request, _('An error occurred while processing your Discord account.'))
return redirect("services:services")
@@ -57,10 +59,10 @@ def discord_callback(request):
return redirect("services:services")
if DiscordTasks.add_user(request.user, code):
logger.info("Successfully activated Discord for user %s" % request.user)
messages.success(request, 'Activated Discord account.')
messages.success(request, _('Activated Discord account.'))
else:
logger.error("Failed to activate Discord for user %s" % request.user)
messages.error(request, 'An error occurred while processing your Discord account.')
messages.error(request, _('An error occurred while processing your Discord account.'))
return redirect("services:services")

View File

@@ -2,6 +2,7 @@ from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _
from .manager import DiscourseManager
from .tasks import DiscourseTasks
@@ -33,12 +34,12 @@ def discourse_sso(request):
# Check if user has access
if not request.user.has_perm(ACCESS_PERM):
messages.error(request, 'You are not authorized to access Discourse.')
messages.error(request, _('You are not authorized to access Discourse.'))
logger.warning('User %s attempted to access Discourse but does not have permission.' % request.user)
return redirect('authentication:dashboard')
if not request.user.profile.main_character:
messages.error(request, "You must have a main character set to access Discourse.")
messages.error(request, _("You must have a main character set to access Discourse."))
logger.warning('User %s attempted to access Discourse but does not have a main character.' % request.user)
return redirect('authentication:characters')
@@ -48,7 +49,7 @@ def discourse_sso(request):
signature = request.GET.get('sig')
if None in [payload, signature]:
messages.error(request, 'No SSO payload or signature. Please contact support if this problem persists.')
messages.error(request, _('No SSO payload or signature. Please contact support if this problem persists.'))
return redirect('authentication:dashboard')
# Validate the payload
@@ -58,7 +59,7 @@ def discourse_sso(request):
assert 'nonce' in decoded
assert len(payload) > 0
except AssertionError:
messages.error(request, 'Invalid payload. Please contact support if this problem persists.')
messages.error(request, _('Invalid payload. Please contact support if this problem persists.'))
return redirect('authentication:dashboard')
key = str(settings.DISCOURSE_SSO_SECRET).encode('utf-8')
@@ -66,7 +67,7 @@ def discourse_sso(request):
this_signature = h.hexdigest()
if this_signature != signature:
messages.error(request, 'Invalid payload. Please contact support if this problem persists.')
messages.error(request, _('Invalid payload. Please contact support if this problem persists.'))
return redirect('authentication:dashboard')
## Build the return payload

View File

@@ -3,6 +3,7 @@ import logging
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _
from allianceauth.services.forms import ServicePasswordForm
from .manager import Ips4Manager
@@ -27,7 +28,7 @@ def activate_ips4(request):
logger.debug("Updated authserviceinfo for user %s with IPS4 credentials." % request.user)
# update_ips4_groups.delay(request.user.pk)
logger.info("Successfully activated IPS4 for user %s" % request.user)
messages.success(request, 'Activated IPSuite4 account.')
messages.success(request, _('Activated IPSuite4 account.'))
credentials = {
'username': result[0],
'password': result[1],
@@ -36,7 +37,7 @@ def activate_ips4(request):
context={'credentials': credentials, 'service': 'IPSuite4'})
else:
logger.error("Unsuccessful attempt to activate IPS4 for user %s" % request.user)
messages.error(request, 'An error occurred while processing your IPSuite4 account.')
messages.error(request, _('An error occurred while processing your IPSuite4 account.'))
return redirect("services:services")
@@ -49,7 +50,7 @@ def reset_ips4_password(request):
# false we failed
if result != "":
logger.info("Successfully reset IPS4 password for user %s" % request.user)
messages.success(request, 'Reset IPSuite4 password.')
messages.success(request, _('Reset IPSuite4 password.'))
credentials = {
'username': request.user.ips4.username,
'password': result,
@@ -58,7 +59,7 @@ def reset_ips4_password(request):
context={'credentials': credentials, 'service': 'IPSuite4'})
logger.error("Unsuccessful attempt to reset IPS4 password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your IPSuite4 account.')
messages.error(request, _('An error occurred while processing your IPSuite4 account.'))
return redirect("services:services")
@@ -76,10 +77,10 @@ def set_ips4_password(request):
result = Ips4Manager.update_custom_password(request.user.ips4.username, plain_password=password)
if result != "":
logger.info("Successfully set IPS4 password for user %s" % request.user)
messages.success(request, 'Set IPSuite4 password.')
messages.success(request, _('Set IPSuite4 password.'))
else:
logger.error("Failed to install custom IPS4 password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your IPSuite4 account.')
messages.error(request, _('An error occurred while processing your IPSuite4 account.'))
return redirect('services:services')
else:
logger.debug("Request is not type POST - providing empty form.")
@@ -96,9 +97,9 @@ def deactivate_ips4(request):
logger.debug("deactivate_ips4 called by user %s" % request.user)
if Ips4Tasks.delete_user(request.user):
logger.info("Successfully deactivated IPS4 for user %s" % request.user)
messages.success(request, 'Deactivated IPSuite4 account.')
messages.success(request, _('Deactivated IPSuite4 account.'))
else:
logger.error("Unsuccessful attempt to deactivate IPS4 for user %s" % request.user)
messages.error(request, 'An error occurred while processing your IPSuite4 account.')
messages.error(request, _('An error occurred while processing your IPSuite4 account.'))
return redirect("services:services")

View File

@@ -2,6 +2,7 @@ import logging
from django.conf import settings
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from allianceauth import hooks
from allianceauth.services.hooks import ServicesHook, MenuItemHook
@@ -22,7 +23,7 @@ class OpenfireService(ServicesHook):
@property
def title(self):
return "Jabber"
return _("Jabber")
def delete_user(self, user, notify_user=False):
logger.debug('Deleting user %s %s account' % (user, self.name))
@@ -74,7 +75,7 @@ def register_service():
class JabberBroadcast(MenuItemHook):
def __init__(self):
MenuItemHook.__init__(self,
'Jabber Broadcast',
_('Jabber Broadcast'),
'fa fa-lock fa-fw fa-bullhorn',
'openfire:broadcast')
@@ -87,7 +88,7 @@ class JabberBroadcast(MenuItemHook):
class FleetBroadcastFormatter(MenuItemHook):
def __init__(self):
MenuItemHook.__init__(self,
'Fleet Broadcast Formatter',
_('Fleet Broadcast Formatter'),
'fa fa-lock fa-fw fa-space-shuttle',
'services:fleet_format_tool')

View File

@@ -5,8 +5,10 @@ from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import Group
from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _
from allianceauth.services.forms import ServicePasswordForm
from .forms import JabberBroadcastForm
from .manager import OpenfireManager, PingBotException
from .models import OpenfireUser
@@ -30,7 +32,7 @@ def activate_jabber(request):
logger.debug("Updated authserviceinfo for user %s with jabber credentials. Updating groups." % request.user)
OpenfireTasks.update_groups.delay(request.user.pk)
logger.info("Successfully activated jabber for user %s" % request.user)
messages.success(request, 'Activated jabber account.')
messages.success(request, _('Activated jabber account.'))
credentials = {
'username': info[0],
'password': info[1],
@@ -39,7 +41,7 @@ def activate_jabber(request):
context={'credentials': credentials, 'service': 'Jabber'})
else:
logger.error("Unsuccessful attempt to activate jabber for user %s" % request.user)
messages.error(request, 'An error occurred while processing your jabber account.')
messages.error(request, _('An error occurred while processing your jabber account.'))
return redirect("services:services")
@@ -52,7 +54,7 @@ def deactivate_jabber(request):
messages.success(request, 'Deactivated jabber account.')
else:
logger.error("Unsuccessful attempt to deactivate jabber for user %s" % request.user)
messages.error(request, 'An error occurred while processing your jabber account.')
messages.error(request, _('An error occurred while processing your jabber account.'))
return redirect("services:services")
@@ -65,7 +67,7 @@ def reset_jabber_password(request):
# If our username is blank means we failed
if result != "":
logger.info("Successfully reset jabber password for user %s" % request.user)
messages.success(request, 'Reset jabber password.')
messages.success(request, _('Reset jabber password.'))
credentials = {
'username': request.user.openfire.username,
'password': result,
@@ -73,7 +75,7 @@ def reset_jabber_password(request):
return render(request, 'services/service_credentials.html',
context={'credentials': credentials, 'service': 'Jabber'})
logger.error("Unsuccessful attempt to reset jabber for user %s" % request.user)
messages.error(request, 'An error occurred while processing your jabber account.')
messages.error(request, _('An error occurred while processing your jabber account.'))
return redirect("services:services")
@@ -114,7 +116,7 @@ def jabber_broadcast_view(request):
OpenfireManager.send_broadcast_message(group_to_send, message_to_send)
messages.success(request, 'Sent jabber broadcast to %s' % group_to_send)
messages.success(request, _('Sent jabber broadcast to %s' % group_to_send))
logger.info("Sent jabber broadcast on behalf of user %s" % request.user)
except PingBotException as e:
messages.error(request, e)
@@ -143,10 +145,10 @@ def set_jabber_password(request):
result = OpenfireManager.update_user_pass(request.user.openfire.username, password=password)
if result != "":
logger.info("Successfully set jabber password for user %s" % request.user)
messages.success(request, 'Set jabber password.')
messages.success(request, _('Set jabber password.'))
else:
logger.error("Failed to install custom jabber password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your jabber account.')
messages.error(request, _('An error occurred while processing your jabber account.'))
return redirect("services:services")
else:
logger.debug("Request is not type POST - providing empty form.")

View File

@@ -3,8 +3,10 @@ import logging
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _
from allianceauth.services.forms import ServicePasswordForm
from .manager import Phpbb3Manager
from .models import Phpbb3User
from .tasks import Phpbb3Tasks
@@ -29,7 +31,7 @@ def activate_forum(request):
logger.debug("Updated authserviceinfo for user %s with forum credentials. Updating groups." % request.user)
Phpbb3Tasks.update_groups.delay(request.user.pk)
logger.info("Successfully activated forum for user %s" % request.user)
messages.success(request, 'Activated forum account.')
messages.success(request, _('Activated forum account.'))
credentials = {
'username': result[0],
'password': result[1],
@@ -38,7 +40,7 @@ def activate_forum(request):
context={'credentials': credentials, 'service': 'Forum'})
else:
logger.error("Unsuccessful attempt to activate forum for user %s" % request.user)
messages.error(request, 'An error occurred while processing your forum account.')
messages.error(request, _('An error occurred while processing your forum account.'))
return redirect("services:services")
@@ -49,10 +51,10 @@ def deactivate_forum(request):
# false we failed
if Phpbb3Tasks.delete_user(request.user):
logger.info("Successfully deactivated forum for user %s" % request.user)
messages.success(request, 'Deactivated forum account.')
messages.success(request, _('Deactivated forum account.'))
else:
logger.error("Unsuccessful attempt to activate forum for user %s" % request.user)
messages.error(request, 'An error occurred while processing your forum account.')
messages.error(request, _('An error occurred while processing your forum account.'))
return redirect("services:services")
@@ -66,7 +68,7 @@ def reset_forum_password(request):
# false we failed
if result != "":
logger.info("Successfully reset forum password for user %s" % request.user)
messages.success(request, 'Reset forum password.')
messages.success(request, _('Reset forum password.'))
credentials = {
'username': request.user.phpbb3.username,
'password': result,
@@ -75,7 +77,7 @@ def reset_forum_password(request):
context={'credentials': credentials, 'service': 'Forum'})
logger.error("Unsuccessful attempt to reset forum password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your forum account.')
messages.error(request, _('An error occurred while processing your forum account.'))
return redirect("services:services")
@@ -95,10 +97,10 @@ def set_forum_password(request):
password=password)
if result != "":
logger.info("Successfully set forum password for user %s" % request.user)
messages.success(request, 'Set forum password.')
messages.success(request, _('Set forum password.'))
else:
logger.error("Failed to install custom forum password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your forum account.')
messages.error(request, _('An error occurred while processing your forum account.'))
return redirect("services:services")
else:
logger.debug("Request is not type POST - providing empty form.")

View File

@@ -3,8 +3,10 @@ import logging
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _
from allianceauth.services.forms import ServicePasswordForm
from .manager import SmfManager
from .models import SmfUser
from .tasks import SmfTasks
@@ -29,7 +31,7 @@ def activate_smf(request):
logger.debug("Updated authserviceinfo for user %s with smf credentials. Updating groups." % request.user)
SmfTasks.update_groups.delay(request.user.pk)
logger.info("Successfully activated smf for user %s" % request.user)
messages.success(request, 'Activated SMF account.')
messages.success(request, _('Activated SMF account.'))
credentials = {
'username': result[0],
'password': result[1],
@@ -38,7 +40,7 @@ def activate_smf(request):
context={'credentials': credentials, 'service': 'SMF'})
else:
logger.error("Unsuccessful attempt to activate smf for user %s" % request.user)
messages.error(request, 'An error occurred while processing your SMF account.')
messages.error(request, _('An error occurred while processing your SMF account.'))
return redirect("services:services")
@@ -50,10 +52,10 @@ def deactivate_smf(request):
# false we failed
if result:
logger.info("Successfully deactivated smf for user %s" % request.user)
messages.success(request, 'Deactivated SMF account.')
messages.success(request, _('Deactivated SMF account.'))
else:
logger.error("Unsuccessful attempt to activate smf for user %s" % request.user)
messages.error(request, 'An error occurred while processing your SMF account.')
messages.error(request, _('An error occurred while processing your SMF account.'))
return redirect("services:services")
@@ -67,7 +69,7 @@ def reset_smf_password(request):
# false we failed
if result != "":
logger.info("Successfully reset smf password for user %s" % request.user)
messages.success(request, 'Reset SMF password.')
messages.success(request, _('Reset SMF password.'))
credentials = {
'username': request.user.smf.username,
'password': result,
@@ -75,7 +77,7 @@ def reset_smf_password(request):
return render(request, 'services/service_credentials.html',
context={'credentials': credentials, 'service': 'SMF'})
logger.error("Unsuccessful attempt to reset smf password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your SMF account.')
messages.error(request, _('An error occurred while processing your SMF account.'))
return redirect("services:services")
@@ -95,10 +97,10 @@ def set_smf_password(request):
password=password)
if result != "":
logger.info("Successfully set smf password for user %s" % request.user)
messages.success(request, 'Set SMF password.')
messages.success(request, _('Set SMF password.'))
else:
logger.error("Failed to install custom smf password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your SMF account.')
messages.error(request, _('An error occurred while processing your SMF account.'))
return redirect("services:services")
else:
logger.debug("Request is not type POST - providing empty form.")

View File

@@ -4,6 +4,8 @@ from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render, redirect
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from .manager import Teamspeak3Manager
from .forms import TeamspeakJoinForm
from .models import Teamspeak3User
@@ -29,10 +31,10 @@ def activate_teamspeak3(request):
Teamspeak3User.objects.update_or_create(user=request.user, defaults={'uid': result[0], 'perm_key': result[1]})
logger.debug("Updated authserviceinfo for user %s with TS3 credentials. Updating groups." % request.user)
logger.info("Successfully activated TS3 for user %s" % request.user)
messages.success(request, 'Activated TeamSpeak3 account.')
messages.success(request, _('Activated TeamSpeak3 account.'))
return redirect("teamspeak3:verify")
logger.error("Unsuccessful attempt to activate TS3 for user %s" % request.user)
messages.error(request, 'An error occurred while processing your TeamSpeak3 account.')
messages.error(request, _('An error occurred while processing your TeamSpeak3 account.'))
return redirect("services:services")
@@ -66,10 +68,10 @@ def deactivate_teamspeak3(request):
logger.debug("deactivate_teamspeak3 called by user %s" % request.user)
if Teamspeak3Tasks.has_account(request.user) and Teamspeak3Tasks.delete_user(request.user):
logger.info("Successfully deactivated TS3 for user %s" % request.user)
messages.success(request, 'Deactivated TeamSpeak3 account.')
messages.success(request, _('Deactivated TeamSpeak3 account.'))
else:
logger.error("Unsuccessful attempt to deactivate TS3 for user %s" % request.user)
messages.error(request, 'An error occurred while processing your TeamSpeak3 account.')
messages.error(request, _('An error occurred while processing your TeamSpeak3 account.'))
return redirect("services:services")
@@ -92,8 +94,8 @@ def reset_teamspeak3_perm(request):
logger.debug("Updated authserviceinfo for user %s with TS3 credentials. Updating groups." % request.user)
Teamspeak3Tasks.update_groups.delay(request.user.pk)
logger.info("Successfully reset TS3 permission key for user %s" % request.user)
messages.success(request, 'Reset TeamSpeak3 permission key.')
messages.success(request, _('Reset TeamSpeak3 permission key.'))
else:
logger.error("Unsuccessful attempt to reset TS3 permission key for user %s" % request.user)
messages.error(request, 'An error occurred while processing your TeamSpeak3 account.')
messages.error(request, _('An error occurred while processing your TeamSpeak3 account.'))
return redirect("services:services")

View File

@@ -3,8 +3,10 @@ import logging
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _
from allianceauth.services.forms import ServicePasswordForm
from .manager import XenForoManager
from .models import XenforoUser
from .tasks import XenforoTasks
@@ -25,7 +27,7 @@ def activate_xenforo_forum(request):
if result['response']['status_code'] == 200:
XenforoUser.objects.update_or_create(user=request.user, defaults={'username': result['username']})
logger.info("Updated user %s with XenForo credentials. Updating groups." % request.user)
messages.success(request, 'Activated XenForo account.')
messages.success(request, _('Activated XenForo account.'))
credentials = {
'username': result['username'],
'password': result['password'],
@@ -35,7 +37,7 @@ def activate_xenforo_forum(request):
else:
logger.error("Unsuccessful attempt to activate xenforo for user %s" % request.user)
messages.error(request, 'An error occurred while processing your XenForo account.')
messages.error(request, _('An error occurred while processing your XenForo account.'))
return redirect("services:services")
@@ -45,9 +47,9 @@ def deactivate_xenforo_forum(request):
logger.debug("deactivate_xenforo_forum called by user %s" % request.user)
if XenforoTasks.delete_user(request.user):
logger.info("Successfully deactivated XenForo for user %s" % request.user)
messages.success(request, 'Deactivated XenForo account.')
messages.success(request, _('Deactivated XenForo account.'))
else:
messages.error(request, 'An error occurred while processing your XenForo account.')
messages.error(request, _('An error occurred while processing your XenForo account.'))
return redirect("services:services")
@@ -60,7 +62,7 @@ def reset_xenforo_password(request):
# Based on XenAPI's response codes
if result['response']['status_code'] == 200:
logger.info("Successfully reset XenForo password for user %s" % request.user)
messages.success(request, 'Reset XenForo account password.')
messages.success(request, _('Reset XenForo account password.'))
credentials = {
'username': request.user.xenforo.username,
'password': result['password'],
@@ -68,7 +70,7 @@ def reset_xenforo_password(request):
return render(request, 'services/service_credentials.html',
context={'credentials': credentials, 'service': 'XenForo'})
logger.error("Unsuccessful attempt to reset XenForo password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your XenForo account.')
messages.error(request, _('An error occurred while processing your XenForo account.'))
return redirect("services:services")
@@ -86,10 +88,10 @@ def set_xenforo_password(request):
result = XenForoManager.update_user_password(request.user.xenforo.username, password)
if result['response']['status_code'] == 200:
logger.info("Successfully reset XenForo password for user %s" % request.user)
messages.success(request, 'Changed XenForo password.')
messages.success(request, _('Changed XenForo password.'))
else:
logger.error("Failed to install custom XenForo password for user %s" % request.user)
messages.error(request, 'An error occurred while processing your XenForo account.')
messages.error(request, _('An error occurred while processing your XenForo account.'))
return redirect('services:services')
else:
logger.debug("Request is not type POST - providing empty form.")

View File

@@ -0,0 +1,17 @@
from django.test import TestCase
from allianceauth.tests.auth_utils import AuthUtils
from ..models import NameFormatConfig
class TestNameFormatConfig(TestCase):
def test_str(self):
obj = NameFormatConfig.objects.create(
service_name='mumble',
format='{{character_name}}'
)
obj.states.add(AuthUtils.get_member_state())
obj.states.add(AuthUtils.get_guest_state())
expected = 'mumble: Member, Guest'
self.assertEqual(str(obj), expected)

View File

@@ -8,7 +8,7 @@ from django.contrib.humanize.templatetags.humanize import intcomma
from django.http import JsonResponse, Http404
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.db.models import Sum
from allianceauth.authentication.decorators import permissions_required
from allianceauth.eveonline.providers import provider

View File

@@ -1,4 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://github.com/ErikKalkoken/filterDropDown/blob/master/js/filterDropDown.min.js
HostUrl=https://raw.githubusercontent.com/ErikKalkoken/filterDropDown/master/js/filterDropDown.min.js

View File

@@ -1,27 +1,45 @@
from allianceauth.authentication.models import UserProfile, State, get_guest_state
from allianceauth.authentication.signals import state_member_alliances_changed, state_member_characters_changed, \
state_member_corporations_changed, state_saved
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import User, Group, Permission
from django.db.models.signals import m2m_changed, pre_save, post_save
from django.test import TestCase
from esi.models import Token
from allianceauth.eveonline.models import EveCharacter
from allianceauth.services.signals import m2m_changed_group_permissions, m2m_changed_user_permissions, \
m2m_changed_state_permissions
from allianceauth.services.signals import m2m_changed_user_groups, disable_services_on_inactive
from esi.models import Token
from allianceauth.authentication.models import (
UserProfile, State, get_guest_state
)
from allianceauth.eveonline.models import EveCharacter
from allianceauth.authentication.signals import (
state_member_alliances_changed,
state_member_characters_changed,
state_member_corporations_changed,
state_saved,
reassess_on_profile_save,
assign_state_on_active_change,
check_state_on_character_update
)
from allianceauth.services.signals import (
m2m_changed_group_permissions,
m2m_changed_user_permissions,
m2m_changed_state_permissions,
m2m_changed_user_groups, disable_services_on_inactive
)
class AuthUtils:
def __init__(self):
pass
"""Utilities for making it easier to create tests for Alliance Auth"""
@staticmethod
def _create_user(username):
def _create_user(username):
return User.objects.create(username=username)
@classmethod
def create_user(cls, username, disconnect_signals=False):
"""create a new user
username: Name of the user
disconnect_signals: whether to run process without signals
"""
if disconnect_signals:
cls.disconnect_signals()
user = cls._create_user(username)
@@ -95,6 +113,11 @@ class AuthUtils:
m2m_changed.disconnect(state_member_characters_changed, sender=State.member_characters.through)
m2m_changed.disconnect(state_member_alliances_changed, sender=State.member_alliances.through)
post_save.disconnect(state_saved, sender=State)
post_save.disconnect(reassess_on_profile_save, sender=UserProfile)
pre_save.disconnect(assign_state_on_active_change, sender=User)
post_save.disconnect(
check_state_on_character_update, sender=EveCharacter
)
@classmethod
def connect_signals(cls):
@@ -107,6 +130,9 @@ class AuthUtils:
m2m_changed.connect(state_member_characters_changed, sender=State.member_characters.through)
m2m_changed.connect(state_member_alliances_changed, sender=State.member_alliances.through)
post_save.connect(state_saved, sender=State)
post_save.connect(reassess_on_profile_save, sender=UserProfile)
pre_save.connect(assign_state_on_active_change, sender=User)
post_save.connect(check_state_on_character_update, sender=EveCharacter)
@classmethod
def add_main_character(cls, user, name, character_id, corp_id='', corp_name='', corp_ticker='', alliance_id='',
@@ -122,6 +148,40 @@ class AuthUtils:
)
UserProfile.objects.update_or_create(user=user, defaults={'main_character': char})
@classmethod
def add_main_character_2(
cls,
user,
name,
character_id,
corp_id='',
corp_name='',
corp_ticker='',
alliance_id='',
alliance_name='',
disconnect_signals=False
):
"""new version that works in all cases"""
if disconnect_signals:
cls.disconnect_signals()
char = EveCharacter.objects.create(
character_id=character_id,
character_name=name,
corporation_id=corp_id,
corporation_name=corp_name,
corporation_ticker=corp_ticker,
alliance_id=alliance_id,
alliance_name=alliance_name,
)
user.profile.main_character = char
user.profile.save()
if disconnect_signals:
cls.connect_signals()
return char
@classmethod
def add_permissions_to_groups(cls, perms, groups, disconnect_signals=True):
if disconnect_signals:
@@ -130,14 +190,67 @@ class AuthUtils:
for group in groups:
for perm in perms:
group.permissions.add(perm)
group = Group.objects.get(pk=group.pk) # reload permission cache
if disconnect_signals:
cls.connect_signals()
@classmethod
def add_permissions_to_state(cls, perms, states, disconnect_signals=True):
return cls.add_permissions_to_groups(perms, states, disconnect_signals=disconnect_signals)
return cls.add_permissions_to_groups(
perms, states, disconnect_signals=disconnect_signals
)
@classmethod
def add_permissions_to_user(cls, perms, user, disconnect_signals=True):
"""add list of permissions to user
perms: list of Permission objects
user: user object
disconnect_signals: whether to run process without signals
"""
if disconnect_signals:
cls.disconnect_signals()
for perm in perms:
user.user_permissions.add(perm)
user = User.objects.get(pk=user.pk) # reload permission cache
if disconnect_signals:
cls.connect_signals()
@classmethod
def add_permission_to_user_by_name(
cls, perm, user, disconnect_signals=True
):
"""returns permission specified by qualified name
perm: Permission name as 'app_label.codename'
user: user object
disconnect_signals: whether to run process without signals
"""
p = cls.get_permission_by_name(perm)
cls.add_permissions_to_user([p], user, disconnect_signals)
@staticmethod
def get_permission_by_name(perm: str) -> Permission:
"""returns permission specified by qualified name
perm: Permission name as 'app_label.codename'
Returns: Permission object or throws exception if not found
"""
perm_parts = perm.split('.')
if len(perm_parts) != 2:
raise ValueError('Invalid format for permission name')
return Permission.objects.get(
content_type__app_label=perm_parts[0], codename=perm_parts[1]
)
class BaseViewTestCase(TestCase):
def setUp(self):

View File

@@ -0,0 +1,60 @@
from unittest import mock
from django.contrib.auth.models import User, Group, Permission
from django.test import TestCase
from allianceauth.eveonline.models import (
EveCorporationInfo, EveAllianceInfo, EveCharacter
)
from .auth_utils import AuthUtils
class TestAuthUtils(TestCase):
def test_can_create_user(self):
user = AuthUtils.create_user('Bruce Wayne')
self.assertTrue(User.objects.filter(username='Bruce Wayne').exists())
def test_can_add_main_character_2(self):
user = AuthUtils.create_user('Bruce Wayne')
character = AuthUtils.add_main_character_2(
user,
name='Bruce Wayne',
character_id=1001,
corp_id=2001,
corp_name='Wayne Technologies',
corp_ticker='WYT',
alliance_id=3001,
alliance_name='Wayne Enterprises'
)
expected = character
self.assertEqual(user.profile.main_character, expected)
def test_can_add_permission_to_group(self):
group = Group.objects.create(name='Dummy Group')
p = AuthUtils.get_permission_by_name('auth.group_management')
AuthUtils.add_permissions_to_groups([p], [group])
self.assertTrue(group.permissions.filter(pk=p.pk).exists())
def test_can_add_permission_to_user_by_name(self):
user = AuthUtils.create_user('Bruce Wayne')
AuthUtils.add_permission_to_user_by_name(
'auth.timer_management', user
)
self.assertTrue(user.has_perm('auth.timer_management'))
class TestGetPermissionByName(TestCase):
def test_can_get_permission_by_name(self):
expected = Permission.objects.get(
content_type__app_label='auth', codename='timer_management'
)
self.assertEqual(
AuthUtils.get_permission_by_name('auth.timer_management'), expected
)
def test_raises_exception_on_invalid_permission_format(self):
with self.assertRaises(ValueError):
AuthUtils.get_permission_by_name('timer_management')

View File

@@ -1,4 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://i.imgur.com/6dXZ5BH.png
HostUrl=https://i.imgur.com/6dXZ5BH.png

View File

@@ -1,4 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://i.imgur.com/Er1g02v.png
HostUrl=https://i.imgur.com/Er1g02v.png

View File

@@ -9,4 +9,5 @@ This section describes how to extend **Alliance Auth** with custom apps and serv
integrating-services
menu-hooks
url-hooks
logging
```

View File

@@ -0,0 +1,15 @@
# Logging from Custom Apps
Alliance Auth provides a logger for use with custom apps to make everyone's life a little easier.
## Using the Extensions Logger
AllianceAuth provides a helper function to get the logger for the current module to reduce the amount of
code you need to write.
```python
from allianceauth.services.hooks import get_extension_logger
logger = get_extension_logger(__name__)
```
This works by creating a child logger of the extension logger which propagates all log entries
to the parent (extensions) logger.

View File

@@ -1,6 +1,6 @@
# Overview
**Alliance Auth** (AA) is a web application that helps Eve Online organizations efficiently manage access to external services and web apps.
**Alliance Auth** (AA) is a web site that helps Eve Online organizations efficiently manage access to applications and external services.
It has the following key features:
@@ -14,6 +14,4 @@ It has the following key features:
- Can be easily extended with additional services and apps. Many are provided by the [community](/features/community/index).
Here is an example how the main page of the web site looks:
![dashboard](/_static/images/features/core/dashboard/dashboard.png)
- Chinese, English, German and Spanish localization

View File

@@ -3,7 +3,9 @@
Welcome to the official documentation for **Alliance Auth**!
**Alliance Auth** is a web application that helps Eve Online organizations efficiently manage access to external services and web apps.
![dashboard](/_static/images/features/core/dashboard/dashboard.png)
**Alliance Auth** is a web site that helps Eve Online organizations efficiently manage access to applications and external services.
```eval_rst
.. toctree::

View File

@@ -9,7 +9,7 @@ This document describes how to install **Alliance Auth** from scratch.
```eval_rst
.. note::
There are additional installation steps for activating services and apps that come with **Alliance Auth**. Please see the page for the respective service or apps in chapter [Features](/features/index) for details.
There are additional installation steps for activating services and apps that come with **Alliance Auth**. Please see the page for the respective service or apps in chapter **Features** for details.
```
## Dependencies
@@ -44,7 +44,14 @@ yum install python36u python36u-devel python36u-setuptools python36u-pip
### Database
It's recommended to use a database service instead of SQLite. Many options are available, but this guide will use MariaDB. Note that Alliance Auth requires Maria DB 10.2.x or higher.
It's recommended to use a database service instead of SQLite. Many options are available, but this guide will use MariaDB.
```eval_rst
.. warning::
Many Ubuntu distributions come with an older version of Maria DB, which is not compatible with **Alliance Auth**. You need Maria DB 10.3 or higher!
For instructions on how To install a newer version of Maria DB on Ubuntu visit this page: `MariaDB Repositories <https://downloads.mariadb.org/mariadb/repositories/#distro=Ubuntu&mirror=osuosl>`_.
```
Ubuntu:
@@ -188,6 +195,12 @@ You can install **Alliance Auth** with the following command. This will install
pip install allianceauth
```
You should also install Gunicorn now unless you want to use another WSGI server (see [Gunicorn](#gunicorn) for details):
```bash
pip install gunicorn
```
Now you need to create the application that will run the **Alliance Auth** install. Ensure you are in the allianceserver home directory by issuing:
```bash
@@ -237,16 +250,15 @@ To run the **Alliance Auth** website a [WSGI Server](https://www.fullstackpython
The default configuration is good enough for most installations. Additional information is available in the [gunicorn](gunicorn.md) doc.
Use this command to install Gunicorn:
```bash
pip install gunicorn
```
### Supervisor
[Supervisor](http://supervisord.org/) is a process watchdog service: it makes sure other processes are started automatically and kept running. It can be used to automatically start the WSGI server and Celery workers for background tasks. Installation varies by OS:
```eval_rst
.. note::
Many package managers will install Supervisor 3 by default, which requires Python 2.
```
Ubuntu:
```bash

View File

@@ -20,7 +20,7 @@ Check out the full [Gunicorn docs](http://docs.gunicorn.org/en/latest/index.html
Install Gunicorn using pip, `pip install gunicorn`.
In your `myauth` base directory, try running `gunicorn --bind 0.0.0.0:8000 myauth.wsgi`. You should be able to browse to http://yourserver:8000 and see your Alliance Auth installation running. Images and styling will be missing, but don't worry, your web server will provide them.
In your `myauth` base directory, try running `gunicorn --bind 0.0.0.0:8000 myauth.wsgi`. You should be able to browse to `http://yourserver:8000` and see your Alliance Auth installation running. Images and styling will be missing, but don't worry, your web server will provide them.
Once you validate its running, you can kill the process with Ctrl+C and continue.
@@ -30,28 +30,30 @@ You should use [Supervisor](allianceauth.md#supervisor) to keep all of Alliance
### Sample Supervisor config
You'll want to edit `/etc/supervisor/conf.d/myauth_gunicorn.conf` (or whatever you want to call the config file)
You'll want to edit `/etc/supervisor/conf.d/myauth.conf` (or whatever you want to call the config file)
```text
[program:myauth-gunicorn]
[program:gunicorn]
user = allianceserver
directory=/home/allianceserver/myauth/
command=gunicorn myauth.wsgi --workers=3 --timeout 120
command=/home/allianceserver/venv/auth/bin/gunicorn myauth.wsgi --workers=3 --timeout 120
stdout_logfile=/home/allianceserver/myauth/log/gunicorn.log
stderr_logfile=/home/allianceserver/myauth/log/gunicorn.log
autostart=true
autorestart=true
stopsignal=INT
```
- `[program:myauth-gunicorn]` - Change myauth-gunicorn to whatever you wish to call your process in Supervisor.
- `[program:gunicorn]` - Change `gunicorn` to whatever you wish to call your process in Supervisor.
- `user = allianceserver` - Change to whatever user you wish Gunicorn to run as. You could even set this as allianceserver if you wished. I'll leave the question security of that up to you.
- `directory=/home/allianceserver/myauth/` - Needs to be the path to your Alliance Auth project.
- `command=gunicorn myauth.wsgi --workers=3 --timeout 120` - Running Gunicorn and the options to launch with. This is where you have some decisions to make, we'll continue below.
- `command=/home/allianceserver/venv/auth/bin/gunicorn myauth.wsgi --workers=3 --timeout 120` - Running Gunicorn and the options to launch with. This is where you have some decisions to make, we'll continue below.
#### Gunicorn Arguments
See the [Commonly Used Arguments](http://docs.gunicorn.org/en/latest/run.html#commonly-used-arguments) or [Full list of settings](http://docs.gunicorn.org/en/stable/settings.html) for more information.
##### Where to bind Gunicorn to?
##### Where to bind Gunicorn to
What address are you going to use to reference it? By default, without a bind parameter, Gunicorn will bind to `127.0.0.1:8000`. This might be fine for your application. If it clashes with another application running on that port you will need to change it. I would suggest using UNIX sockets too, if you can.
@@ -63,9 +65,9 @@ Whatever you decide to use, remember it because we'll need it when configuring y
##### Number of workers
By default Gunicorn will spawn only one worker. The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests between 2-4 workers per core. Really you could probably get away with 2-4 in total for most installs.
By default Gunicorn will spawn only one worker. The number you set this to will depend on your own server environment, how many visitors you have etc. Gunicorn suggests `(2 x $num_cores) + 1` for the number of workers. So for example if you have 2 cores you want 2 x 2 + 1 = 5 workers. See [here](https://docs.gunicorn.org/en/stable/design.html#how-many-workers) for the official discussion on this topic.
Change it by adding `--workers=2` to the command.
Change it by adding `--workers=5` to the command.
##### Running with a virtual environment
@@ -73,9 +75,11 @@ If you're running with a virtual environment, you'll need to add the path to the
e.g. `command=/path/to/venv/bin/gunicorn myauth.wsgi`
The example config is using the myauth venv from the main installation guide: `command=/home/allianceserver/venv/auth/bin/gunicorn myauth.wsgi`
### Starting via Supervisor
Once you have your configuration all sorted, you will need to reload your supervisor config `service supervisor reload` and then you can start the Gunicorn server via `supervisorctl start aauth-gunicorn` (or whatever you renamed it to). You should see something like the following `aauth-gunicorn: started`. If you get some other message, you'll need to consult the Supervisor log files, usually found in `/var/log/supervisor/`.
Once you have your configuration all sorted, you will need to reload your supervisor config `service supervisor reload` and then you can start the Gunicorn server via `supervisorctl start myauth:gunicorn` (or whatever you renamed it to). You should see something like the following `myauth-gunicorn: started`. If you get some other message, you'll need to consult the Supervisor log files, usually found in `/var/log/supervisor/`.
## Configuring your webserver
@@ -85,4 +89,4 @@ Any web server capable of proxy passing should be able to sit in front of Gunico
In the past when you made changes you restarted the entire Apache server. This is no longer required. When you update or make configuration changes that ask you to restart Apache, instead you can just restart Gunicorn:
`supervisorctl restart myauth-gunicorn`, or the service name you chose for it.
`supervisorctl restart gunicorn`, or the service name you chose for it.

View File

@@ -12,5 +12,5 @@ In addition to main guide for installation Alliance Auth you also find guides fo
nginx
apache
gunicorn
upgradev1
upgrade_python
```

View File

@@ -0,0 +1,270 @@
# Upgrading Python 3
This guide describes how to upgrade an existing Alliance Auth (AA) installation to a newer Python 3 version.
```eval_rst
.. hint::
In accordance with the installation guide we will assume you perform all actions as root. If you are not running as root you need to add ``sudo`` to some commands.
```
```eval_rst
.. note::
This guide will upgrade the software components only but not change any data or configuration.
```
## Install a new Python version
To run AA with a newer Python 3 version than your system's default you need to install it first. Technically it would be possible to upgrade your system's default Python 3, but since many of your system's tools have been tested to work with that specific version we would not recommend it. Instead we recommend to install an additional Python 3 version alongside your default version and use that for AA.
```eval_rst
.. note::
For stability and performance we currently recommend to run AA with Python 3.7. Since at the time of writing Python 3.7 was not available for CentOS through yum install this guide will upgrade to Python 3.6. For Ubuntu one can just replace "3.6" with "3.7" in the installation commands to get Python 3.7.
```
To install other Python versions than come with your distro you need to add a new installation repository. Then you can install the specific Python 3 to your system.
Ubuntu:
```bash
add-apt-repository ppa:deadsnakes/ppa
```
```bash
apt-get update
```
```bash
apt-get install python3.6 python3.6-dev python3.6-venv
```
CentOS:
```bash
yum install https://centos7.iuscommunity.org/ius-release.rpm
```
```bash
yum update
```
```bash
yum install python36u python36u-pip python36u-devel
```
## Preparing your venv
Before updating your venv it is important to make sure that your current installation is stable. Otherwise your new venv might not be consistent with your data, which might create problems.
Start by navigating to your main project folder (the one that has `manage.py` in it). If you followed the default installation the path is: `/home/allianceserver/myauth`
Activate your venv:
```bash
source /home/allianceserver/venv/auth/bin/activate
```
### Upgrade AA
Make sure to upgrade AA to the newest version:
```bash
pip install -U allianceauth
```
Run migrations and collectstatic.
```bash
python manage.py migrate
```
```bash
python manage.py collectstatic
```
Restart your AA supervisor:
```bash
supervisorctl restart myauth:
```
### Upgrade your apps
You also need to upgrade all additional apps to their newest version that you have installed. And you need to make sure that you can reinstall all your apps later, e.g. you know from which repo they came. We recommend to make a list of all your apps, so you can just go through them later when you rebuild your venv.
If you unsure which apps you have installed from repos check `INSTALLED_APPS` in your settings. Alternatively run this command to get a list all apps in your venv.
```bash
pip list
```
Some AA installations might still be running an older version of django_celery_beat. We would recommend to upgrade to the current version before doing the Python update:
```bash
pip install -U django_celery_beat
```
```bash
python manage.py migrate
```
Make sure to run migrations and collect static files for all upgraded apps.
### Restart and final check
Do a final restart of your AA supervisors and make sure your installation is still running normally.
For a final check that they are no issues - e.g. any outstanding migrations - run this command:
```bash
python manage.py check
```
If you get the following result you are good to go. Otherwise make sure to fix any issues first before proceeding.
```bash
System check identified no issues (0 silenced).
```
## Backup current venv
Make sure you are in your venv!
First we create a list of all installed packages in your venv. You can use this list later as reference to see what packages should be installed.
```bash
pip freeze > requirements.txt
```
At this point we recommend creating a list of the additional packages that you need to manually reinstall later on top of AA:
- Community AA apps (e.g. aa-structures)
- Additional tools you are using (e.g. flower, django-extensions)
```eval_rst
.. hint::
While `requirements.txt` will contain a complete list of your packages, it will also contain many packages that are automatically installed as dependencies and don't need be manually reinstalled.
```
```eval_rst
.. note::
Some guide on the Internet will suggest to use use the requirements.txt file to recreate a venv. This is indeed possible, but only works if all packages can be installed from PyPI. Since most community apps are installed directly from repos this guide will not follow that approach.
```
Leave the venv and shutdown all AA services:
```bash
deactivate
```
```bash
supervisorctl stop myauth:
```
Rename and keep your old venv so we have a fallback in case of some unforeseeable issues:
```bash
mv /home/allianceserver/venv/auth /home/allianceserver/venv/auth_old
```
## Create your new venv
Now let's create our new venv with Python 3.6 and activate it:
```bash
python3.6 -m venv /home/allianceserver/venv/auth
```
```bash
source /home/allianceserver/venv/auth/bin/activate
```
## Reinstall packages
Now we need to reinstall all packages into your new venv.
### Install basic packages
```bash
pip install --upgrade pip
```
```bash
pip install --upgrade setuptools
```
```bash
pip install wheel
```
### Installing AA & Gunicorn
```bash
pip install allianceauth
```
```bash
pip install gunicorn
```
### Install all other packages
Last, but not least you need to reinstall all other packages, e.g. for AA community apps or additional tools.
Use the list of packages you created earlier as a checklist. Alternatively you use the `requirements.txt` file we created earlier to see what you need. During the installation process you can run `pip list` to see what you already got installed.
To check whether you are missing any apps you can also run the check command:
```bash
python manage.py check
```
Note: In case you forget to install an app you will get this error
```bash
ModuleNotFoundError: No module named 'xyz'
```
Note that you should not need to run any migrations unless you forgot to upgrade one of your existing apps or you got the newer version of an app through a dependency. In that case you just migrations normally.
## Restart
After you have completed installing all packages just start your AA supervisor again.
```bash
supervisorctl start myauth:
```
We recommend to keep your old venv copy for a couple of days so you have a fallback just in case. After that you should be fine to remove it.
## Fallback
In case you run into any major issue you can always switch back to your initial venv.
Before you start double-check that you still have your old venv for auth:
```bash
ls /home/allianceserver/venv/auth /home/allianceserver/venv
```
If the output shows these two folders you should be safe to proceed:
- `auth`
- `auth_old`
Run these commands to remove your current venv and switch back to the old venv for auth:
```bash
supervisorctl stop myauth:
```
```bash
rm -rf /home/allianceserver/venv/auth
```
```bash
mv /home/allianceserver/venv/auth_old /home/allianceserver/venv/auth
```
```bash
supervisorctl start myauth:
```

View File

@@ -1,87 +0,0 @@
# Upgrading from AA v1.15
It's possible to preserve a v1 install's database and migrate it to v2. This will retain all service accounts, user accounts with their main character, but will purge API keys and alts.
## Preparing to Upgrade
Ensure your auth install is at least version 1.15 - it's preferred to be on v1.15.8 but any v1.15.x should work. If not, update following normal procedures and ensure you run migrations.
If you will not be using any apps (or they have been removed in v2) clear their database tables before beginning the v2 install procedure. From within your v1 install, `python manage.py migrate APPNAME zero`, replacing `APPNAME` with the appropriate name.
It's strongly encouraged to perform the upgrade with a **copy** of the original v1 database. If something goes wrong this allows you to roll back and try again. This can be achieved with:
mysqldump -u root -p v1_database_name_here | mysql -u root -p v2_database_name_here
Note this command will prompt you for the root password twice.
Alliance Auth v2 requires Python 3.4 or newer. If this is not available on your system be sure to install the [dependencies listed.](allianceauth.md#python)
## Installation
Follow the [normal install procedures for Alliance Auth v2](allianceauth.md). If you're coming from v1 you likely already have the user account and dependencies.
When configuring settings, ensure to enter the database information relating to the copy you made of the v1 database, but **do not run migrations**. See below before continuing.
Do not start Celery yet.
## Migration
During the upgrade process a migration is run which ports data to the new system. Its behaviour can be configured through some optional settings.
### User Accounts
A database migration is included to port users to the new SSO-based login system. It will automatically assign states and main characters.
Password for non-staff accounts are destroyed so they cannot be used to log in - only SSO is available for regular members. Staff can login using username and password through the admin site or by SSO.
### EVE Characters
Character ownership is now tracked by periodically validating a refreshable SSO token. When migrating from v1 not all users may have such a refreshable token: to allow these users to retain their existing user account their main character is set to the one present in v1, bypassing this validation mechanism.
It is essential to get your users to log in via SSO soon after migration to ensure their accounts are managed properly. Any user who does not log in will not lose their main character under any circumstance, even character sale. During a sale characters are transferred to an NPC corp - get celery tasks running within 24 hours of migration so that during a sale the user loses their state (and hence permissions). This can be an issue if you've granted service access permissions to a user or their groups: it's strongly encouraged to grant service access by state from now on.
Because character ownership is tracked separately of main character it is not possible to preserve alts unless a refreshable SSO token is present for them.
### Members and Blues
The new [state system](../../features/core/states.md) allows configuring dynamic membership states through the admin page. Unfortunately if you make a change after migrating it will immediately assess user states and see that no one should be a member. You can add additional settings to your auth project's settings file to generate the member and blue states as you have them defined in v1:
- `ALLIANCE_IDS = []` a list of member alliance IDs
- `CORP_IDS = []` a list of member corporation IDs
- `BLUE_ALLIANCE_IDS = []` a list of blue alliance IDs
- `BLUE_CORP_IDS = []` a list of blue corporation IDs
Put comma-separated IDs into the brackets and the migration will create states with the members and blues you had before. This will prevent unexpected state purging when you edit states via the admin site.
### Default Groups
If you used member/blue group names other than the standard "Member" and "Blue" you can enter settings to have the member/blue states created through this migration take these names.
- `DEFAULT_AUTH_GROUP = ""` the desired name of the "Member" state
- `DEFAULT_BLUE_GROUP = ""` the desired name of the "Blue" state
Any permissions assigned to these groups will be copied to the state replacing them. Because these groups are no longer managed they pose a security risk and so are deleted at the end of the migration automatically.
### Run Migrations
Once you've configured any optional settings it is now safe to run the included migrations.
## Validating Upgrade
Before starting the Celery workers it's a good idea to validate the states were created and assigned correctly. Any mistakes now will trigger deletion of service accounts: if Celery workers aren't running these tasks aren't yet processed. States can be checked through the admin site.
The site (and not Celery) can be started with `supervisorctl start myauth:gunicorn`. Then navigate to the admin site and log in with your v1 username and password. States and User Profiles can be found under the Authentication app.
Once you have confirmed states are correctly configured (or adjusted them as needed) clear the Celery task queue: from within your auth project folder, `celery -A myauth purge`
It should now be safe to bring workers online (`supervisorctl start myauth:`) and invite users to use the site.
## Securing User Accounts
After migration users will have main characters without a method to check for character sales. After a brief period to allow your users to log in (and provide a refreshable token to use), it's a good idea to clear main characters of users who haven't yet logged in. This can be achieved with an included command: `python manage.py checkmains`
A similar process can be used to ensure users who may have lost service permissions during the migration do not have active service accounts. This is done using `python manage.py validate_service_accounts`.
## Help
If something goes wrong during the migration reach out for help on [Gitter](https://gitter.im/R4stl1n/allianceauth) or open an [issue](https://gitlab.com/allianceauth/allianceauth/issues).

View File

@@ -11,6 +11,15 @@ app = Celery('devauth')
# Using a string here means the worker don't have to serialize
# the configuration object to child processes.
app.config_from_object('django.conf:settings')
# setup priorities ( 0 Highest, 9 Lowest )
app.conf.broker_transport_options = {
'priority_steps': list(range(10)), # setup que to have 10 steps
'queue_order_strategy': 'priority', # setup que to use prio sorting
}
app.conf.task_default_priority = 5 # anything called with the task.delay() will be given normal priority (5)
app.conf.worker_prefetch_multiplier = 1 # only prefetch single tasks at a time on the workers so that prio tasks happen
app.conf.ONCE = {
'backend': 'allianceauth.services.tasks.DjangoBackend',
'settings': {}

View File

@@ -20,3 +20,4 @@ commands =
all: coverage run runtests.py -v 2
all: coverage report -m
core: coverage run runtests.py allianceauth.authentication.tests.test_app_settings -v 2
all: coverage xml