mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-09 12:30:15 +02:00
Merge branch 'master' of https://gitlab.com/allianceauth/allianceauth into v2.10.x
This commit is contained in:
commit
ff610efc84
@ -76,6 +76,27 @@ test-3.10-core:
|
|||||||
reports:
|
reports:
|
||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
|
test-3.11-core:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.11-rc-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e py311-core
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
test-3.7-all:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.7-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e py37-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
|
||||||
test-3.8-all:
|
test-3.8-all:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
image: python:3.8-bullseye
|
image: python:3.8-bullseye
|
||||||
@ -106,6 +127,17 @@ test-3.10-all:
|
|||||||
reports:
|
reports:
|
||||||
cobertura: coverage.xml
|
cobertura: coverage.xml
|
||||||
|
|
||||||
|
test-3.11-all:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.11-rc-bullseye
|
||||||
|
script:
|
||||||
|
- tox -e py311-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
cobertura: coverage.xml
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
deploy_production:
|
deploy_production:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: python:3.10-bullseye
|
image: python:3.10-bullseye
|
||||||
|
@ -13,17 +13,18 @@ from allianceauth import __version__
|
|||||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||||
os.path.abspath(__file__)), 'swagger.json'
|
os.path.abspath(__file__)), 'swagger.json'
|
||||||
)
|
)
|
||||||
"""
|
|
||||||
Swagger spec operations:
|
|
||||||
|
|
||||||
get_alliances_alliance_id
|
# for the love of Bob please add operations you use here. I'm tired of breaking undocumented things.
|
||||||
get_alliances_alliance_id_corporations
|
ESI_OPERATIONS=[
|
||||||
get_corporations_corporation_id
|
'get_alliances_alliance_id',
|
||||||
get_characters_character_id
|
'get_alliances_alliance_id_corporations',
|
||||||
get_universe_types_type_id
|
'get_corporations_corporation_id',
|
||||||
post_character_affiliation
|
'get_characters_character_id',
|
||||||
get_universe_factions
|
'post_characters_affiliation',
|
||||||
"""
|
'get_universe_types_type_id',
|
||||||
|
'get_universe_factions',
|
||||||
|
'post_universe_names',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
File diff suppressed because one or more lines are too long
@ -49,7 +49,6 @@ def run_model_update():
|
|||||||
for alliance in EveAllianceInfo.objects.all().values('alliance_id'):
|
for alliance in EveAllianceInfo.objects.all().values('alliance_id'):
|
||||||
update_alliance.apply_async(args=[alliance['alliance_id']], priority=TASK_PRIORITY)
|
update_alliance.apply_async(args=[alliance['alliance_id']], priority=TASK_PRIORITY)
|
||||||
|
|
||||||
#update existing character models if required
|
|
||||||
# update existing character models
|
# update existing character models
|
||||||
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
|
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
|
||||||
for character_ids_chunk in chunks(character_ids, CHUNK_SIZE):
|
for character_ids_chunk in chunks(character_ids, CHUNK_SIZE):
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
{% if not auto_leave %}
|
||||||
<li>
|
<li>
|
||||||
<a data-toggle="tab" href="#leave">
|
<a data-toggle="tab" href="#leave">
|
||||||
{% translate "Leave Requests" %}
|
{% translate "Leave Requests" %}
|
||||||
@ -38,6 +40,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="panel panel-default panel-tabs-aa">
|
<div class="panel panel-default panel-tabs-aa">
|
||||||
@ -100,6 +103,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if not auto_leave %}
|
||||||
<div id="leave" class="tab-pane">
|
<div id="leave" class="tab-pane">
|
||||||
{% if leaverequests %}
|
{% if leaverequests %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
@ -155,6 +159,7 @@
|
|||||||
<div class="alert alert-warning text-center">{% translate "No group leave requests." %}</div>
|
<div class="alert alert-warning text-center">{% translate "No group leave requests." %}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.test import RequestFactory, TestCase
|
from django.test import RequestFactory, TestCase, override_settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from allianceauth.tests.auth_utils import AuthUtils
|
from allianceauth.tests.auth_utils import AuthUtils
|
||||||
@ -6,14 +6,80 @@ from allianceauth.tests.auth_utils import AuthUtils
|
|||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
|
|
||||||
|
def response_content_to_str(response) -> str:
|
||||||
|
return response.content.decode(response.charset)
|
||||||
|
|
||||||
|
|
||||||
class TestViews(TestCase):
|
class TestViews(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.user = AuthUtils.create_user('Bruce Wayne')
|
self.user = AuthUtils.create_user('Peter Parker')
|
||||||
|
self.user_with_manage_permission = AuthUtils.create_user('Bruce Wayne')
|
||||||
|
|
||||||
|
# set permissions
|
||||||
|
AuthUtils.add_permission_to_user_by_name(
|
||||||
|
'auth.group_management', self.user_with_manage_permission
|
||||||
|
)
|
||||||
|
|
||||||
def test_groups_view_can_load(self):
|
def test_groups_view_can_load(self):
|
||||||
request = self.factory.get(reverse('groupmanagement:groups'))
|
request = self.factory.get(reverse('groupmanagement:groups'))
|
||||||
request.user = self.user
|
request.user = self.user
|
||||||
response = views.groups_view(request)
|
response = views.groups_view(request)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_management_view_can_load_for_user_with_permissions(self):
|
||||||
|
"""
|
||||||
|
Test if user with management permissions can access the management view
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user_with_manage_permission
|
||||||
|
response = views.group_management(request)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_management_view_doesnt_load_for_user_without_permissions(self):
|
||||||
|
"""
|
||||||
|
Test if user without management permissions can't access the management view
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user
|
||||||
|
response = views.group_management(request)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_AUTO_LEAVE=False)
|
||||||
|
def test_leave_requests_tab_visible(self):
|
||||||
|
"""
|
||||||
|
Test if the leave requests tab is visible when GROUPMANAGEMENT_AUTO_LEAVE = False
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user_with_manage_permission
|
||||||
|
response = views.group_management(request)
|
||||||
|
|
||||||
|
content = response_content_to_str(response)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertIn('<a data-toggle="tab" href="#leave">', content)
|
||||||
|
self.assertIn('<div id="leave" class="tab-pane">', content)
|
||||||
|
|
||||||
|
@override_settings(GROUPMANAGEMENT_AUTO_LEAVE=True)
|
||||||
|
def test_leave_requests_tab_hidden(self):
|
||||||
|
"""
|
||||||
|
Test if the leave requests tab is hidden when GROUPMANAGEMENT_AUTO_LEAVE = True
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = self.factory.get(reverse('groupmanagement:management'))
|
||||||
|
request.user = self.user_with_manage_permission
|
||||||
|
response = views.group_management(request)
|
||||||
|
|
||||||
|
content = response_content_to_str(response)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertNotIn('<a data-toggle="tab" href="#leave">', content)
|
||||||
|
self.assertNotIn('<div id="leave" class="tab-pane">', content)
|
||||||
|
@ -45,7 +45,11 @@ def group_management(request):
|
|||||||
logger.debug("Providing user {} with {} acceptrequests and {} leaverequests.".format(
|
logger.debug("Providing user {} with {} acceptrequests and {} leaverequests.".format(
|
||||||
request.user, len(acceptrequests), len(leaverequests)))
|
request.user, len(acceptrequests), len(leaverequests)))
|
||||||
|
|
||||||
render_items = {'acceptrequests': acceptrequests, 'leaverequests': leaverequests}
|
render_items = {
|
||||||
|
'acceptrequests': acceptrequests,
|
||||||
|
'leaverequests': leaverequests,
|
||||||
|
'auto_leave': getattr(settings, 'GROUPMANAGEMENT_AUTO_LEAVE', False),
|
||||||
|
}
|
||||||
|
|
||||||
return render(request, 'groupmanagement/index.html', context=render_items)
|
return render(request, 'groupmanagement/index.html', context=render_items)
|
||||||
|
|
||||||
|
@ -49,19 +49,22 @@ class NotificationManager(models.Manager):
|
|||||||
logger.info("Created notification %s", obj)
|
logger.info("Created notification %s", obj)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def _max_notifications_per_user(self):
|
def _max_notifications_per_user(self) -> int:
|
||||||
"""return the maximum number of notifications allowed per user"""
|
"""Maximum number of notifications allowed per user."""
|
||||||
max_notifications = getattr(settings, 'NOTIFICATIONS_MAX_PER_USER', None)
|
max_notifications = getattr(
|
||||||
if (
|
settings,
|
||||||
max_notifications is None
|
"NOTIFICATIONS_MAX_PER_USER",
|
||||||
or not isinstance(max_notifications, int)
|
self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||||
or max_notifications < 0
|
)
|
||||||
):
|
try:
|
||||||
|
max_notifications = int(max_notifications)
|
||||||
|
except ValueError:
|
||||||
|
max_notifications = None
|
||||||
|
if max_notifications is None or max_notifications < 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'NOTIFICATIONS_MAX_PER_USER setting is invalid. Using default.'
|
"NOTIFICATIONS_MAX_PER_USER setting is invalid. Using default."
|
||||||
)
|
)
|
||||||
max_notifications = self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
max_notifications = self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||||
|
|
||||||
return max_notifications
|
return max_notifications
|
||||||
|
|
||||||
def user_unread_count(self, user_pk: int) -> int:
|
def user_unread_count(self, user_pk: int) -> int:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
@ -113,29 +114,53 @@ class TestUserNotify(TestCase):
|
|||||||
self.assertSetEqual(result, expected)
|
self.assertSetEqual(result, expected)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("allianceauth.notifications.managers.logger")
|
||||||
@patch(
|
@patch(
|
||||||
MODULE_PATH + '.Notification.NOTIFICATIONS_MAX_PER_USER_DEFAULT',
|
MODULE_PATH + ".Notification.NOTIFICATIONS_MAX_PER_USER_DEFAULT",
|
||||||
NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||||
)
|
)
|
||||||
class TestMaxNotificationsPerUser(TestCase):
|
class TestMaxNotificationsPerUser(TestCase):
|
||||||
|
@override_settings(NOTIFICATIONS_MAX_PER_USER=42)
|
||||||
@override_settings(NOTIFICATIONS_MAX_PER_USER=None)
|
def test_should_use_custom_integer_setting(self, mock_logger):
|
||||||
def test_reset_to_default_if_not_defined(self):
|
# when
|
||||||
result = Notification.objects._max_notifications_per_user()
|
result = Notification.objects._max_notifications_per_user()
|
||||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
# then
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, 42)
|
||||||
|
self.assertFalse(mock_logger.warning.called)
|
||||||
|
|
||||||
@override_settings(NOTIFICATIONS_MAX_PER_USER='11')
|
@override_settings(NOTIFICATIONS_MAX_PER_USER="42")
|
||||||
def test_reset_to_default_if_not_int(self):
|
def test_should_use_custom_string_setting(self, mock_logger):
|
||||||
|
# when
|
||||||
result = Notification.objects._max_notifications_per_user()
|
result = Notification.objects._max_notifications_per_user()
|
||||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
# then
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, 42)
|
||||||
|
self.assertFalse(mock_logger.warning.called)
|
||||||
|
|
||||||
|
@override_settings()
|
||||||
|
def test_should_use_default_if_not_defined(self, mock_logger):
|
||||||
|
# given
|
||||||
|
del settings.NOTIFICATIONS_MAX_PER_USER
|
||||||
|
# when
|
||||||
|
result = Notification.objects._max_notifications_per_user()
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, NOTIFICATIONS_MAX_PER_USER_DEFAULT)
|
||||||
|
self.assertFalse(mock_logger.warning.called)
|
||||||
|
|
||||||
|
@override_settings(NOTIFICATIONS_MAX_PER_USER="abc")
|
||||||
|
def test_should_reset_to_default_if_not_int(self, mock_logger):
|
||||||
|
# when
|
||||||
|
result = Notification.objects._max_notifications_per_user()
|
||||||
|
# then
|
||||||
|
self.assertEqual(result, NOTIFICATIONS_MAX_PER_USER_DEFAULT)
|
||||||
|
self.assertTrue(mock_logger.warning.called)
|
||||||
|
|
||||||
@override_settings(NOTIFICATIONS_MAX_PER_USER=-1)
|
@override_settings(NOTIFICATIONS_MAX_PER_USER=-1)
|
||||||
def test_reset_to_default_if_lt_zero(self):
|
def test_should_reset_to_default_if_lt_zero(self, mock_logger):
|
||||||
|
# when
|
||||||
result = Notification.objects._max_notifications_per_user()
|
result = Notification.objects._max_notifications_per_user()
|
||||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
# then
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, NOTIFICATIONS_MAX_PER_USER_DEFAULT)
|
||||||
|
self.assertTrue(mock_logger.warning.called)
|
||||||
|
|
||||||
|
|
||||||
@patch('allianceauth.notifications.managers.cache')
|
@patch('allianceauth.notifications.managers.cache')
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
6
allianceauth/static/js/jquery-ui/1.12.1/css/jquery-ui.min.css
vendored
Normal file
6
allianceauth/static/js/jquery-ui/1.12.1/css/jquery-ui.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,3 +1,5 @@
|
|||||||
<!-- Start jQuery UI CSS from cdnjs -->
|
{% load static %}
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" integrity="sha512-aOG0c6nPNzGk+5zjwyJaoRUgCdOrfSDhmMID2u4+OIslr0GjpLKo7Xm0Ao3xmpM4T8AmIouRkqwj1nrdVsLKEQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<!-- Start jQuery UI CSS from Alliance Auth -->
|
||||||
<!-- End jQuery UI CSS from cdnjs -->
|
<!-- CDNs all contain theme.css, which is not supposed to be in the base CSS, Which is why this is uniquely bundled in not using a CDN -->
|
||||||
|
<link rel="stylesheet" href="{% static 'js/jquery-ui/1.12.1/css/jquery-ui.min.css' %}" integrity="sha512-7smZe1765O+Mm1UZH46SzaFClbRX7dEs01lB9lqU91oocmugWWfQXVQNVr5tEwktYSqwJMErEfr4GvflXMgTPA==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
||||||
|
<!-- End jQuery UI CSS from aa-gdpr -->
|
||||||
|
@ -52,7 +52,7 @@ services:
|
|||||||
- auth_mysql
|
- auth_mysql
|
||||||
|
|
||||||
grafana:
|
grafana:
|
||||||
image: grafana/grafana:8.2
|
image: grafana/grafana-oss:8.2
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- auth_mysql
|
- auth_mysql
|
||||||
|
@ -90,11 +90,16 @@ This allows you to more finely control who has access to manage which groups.
|
|||||||
|
|
||||||
### Auto Leave
|
### Auto Leave
|
||||||
|
|
||||||
By default in AA, Both requests and leaves for non-open groups must be approved by a group manager. If you wish to allow users to leave groups without requiring approvals, add the following lines to your `local.py`
|
By default, in AA both requests and leaves for non-open groups must be approved by a group manager. If you wish to allow users to leave groups without requiring approvals, add the following lines to your `local.py`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
## Allows users to freely leave groups without requiring approval.
|
## Allows users to freely leave groups without requiring approval.
|
||||||
AUTO_LEAVE = True
|
GROUPMANAGEMENT_AUTO_LEAVE = True
|
||||||
|
```
|
||||||
|
|
||||||
|
```eval_rst
|
||||||
|
.. note::
|
||||||
|
Before you set `GROUPMANAGEMENT_AUTO_LEAVE = True`, make sure there are no pending leave requests, as this option will hide the "Leave Requests" tab.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
3
tox.ini
3
tox.ini
@ -1,7 +1,7 @@
|
|||||||
[tox]
|
[tox]
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
usedevelop = true
|
usedevelop = true
|
||||||
envlist = py{38,39,310}-{all,core}
|
envlist = py{38,39,310,311}-{all,core}
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
setenv =
|
setenv =
|
||||||
@ -12,6 +12,7 @@ basepython =
|
|||||||
py38: python3.8
|
py38: python3.8
|
||||||
py39: python3.9
|
py39: python3.9
|
||||||
py310: python3.10
|
py310: python3.10
|
||||||
|
py311: python3.11
|
||||||
deps=
|
deps=
|
||||||
coverage
|
coverage
|
||||||
install_command = pip install -e ".[testing]" -U {opts} {packages}
|
install_command = pip install -e ".[testing]" -U {opts} {packages}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user