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:
|
||||
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:
|
||||
<<: *only-default
|
||||
image: python:3.8-bullseye
|
||||
@ -106,6 +127,17 @@ test-3.10-all:
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
|
||||
test-3.11-all:
|
||||
<<: *only-default
|
||||
image: python:3.11-rc-bullseye
|
||||
script:
|
||||
- tox -e py311-all
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
cobertura: coverage.xml
|
||||
allow_failure: true
|
||||
|
||||
deploy_production:
|
||||
stage: deploy
|
||||
image: python:3.10-bullseye
|
||||
|
@ -13,17 +13,18 @@ from allianceauth import __version__
|
||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(
|
||||
os.path.abspath(__file__)), 'swagger.json'
|
||||
)
|
||||
"""
|
||||
Swagger spec operations:
|
||||
|
||||
get_alliances_alliance_id
|
||||
get_alliances_alliance_id_corporations
|
||||
get_corporations_corporation_id
|
||||
get_characters_character_id
|
||||
get_universe_types_type_id
|
||||
post_character_affiliation
|
||||
get_universe_factions
|
||||
"""
|
||||
# for the love of Bob please add operations you use here. I'm tired of breaking undocumented things.
|
||||
ESI_OPERATIONS=[
|
||||
'get_alliances_alliance_id',
|
||||
'get_alliances_alliance_id_corporations',
|
||||
'get_corporations_corporation_id',
|
||||
'get_characters_character_id',
|
||||
'post_characters_affiliation',
|
||||
'get_universe_types_type_id',
|
||||
'get_universe_factions',
|
||||
'post_universe_names',
|
||||
]
|
||||
|
||||
|
||||
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'):
|
||||
update_alliance.apply_async(args=[alliance['alliance_id']], priority=TASK_PRIORITY)
|
||||
|
||||
#update existing character models if required
|
||||
# update existing character models
|
||||
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
|
||||
for character_ids_chunk in chunks(character_ids, CHUNK_SIZE):
|
||||
|
@ -29,15 +29,18 @@
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a data-toggle="tab" href="#leave">
|
||||
{% translate "Leave Requests" %}
|
||||
|
||||
{% if leaverequests %}
|
||||
<span class="badge">{{ leaverequests|length }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% if not auto_leave %}
|
||||
<li>
|
||||
<a data-toggle="tab" href="#leave">
|
||||
{% translate "Leave Requests" %}
|
||||
|
||||
{% if leaverequests %}
|
||||
<span class="badge">{{ leaverequests|length }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<div class="panel panel-default panel-tabs-aa">
|
||||
@ -100,61 +103,63 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="leave" class="tab-pane">
|
||||
{% if leaverequests %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th>{% translate "Organization" %}</th>
|
||||
<th>{% translate "Group" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for leaverequest in leaverequests %}
|
||||
{% if not auto_leave %}
|
||||
<div id="leave" class="tab-pane">
|
||||
{% if leaverequests %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;">
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.character_name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ leaverequest.user.username }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.corporation_name }}
|
||||
</a><br>
|
||||
{{ leaverequest.main_char.alliance_name|default_if_none:"" }}
|
||||
{% else %}
|
||||
{% translate "(unknown)" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ leaverequest.group.name }}</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url 'groupmanagement:leave_accept_request' leaverequest.id %}" class="btn btn-success">
|
||||
{% translate "Accept" %}
|
||||
</a>
|
||||
|
||||
<a href="{% url 'groupmanagement:leave_reject_request' leaverequest.id %}" class="btn btn-danger">
|
||||
{% translate "Reject" %}
|
||||
</a>
|
||||
</td>
|
||||
<th>{% translate "Character" %}</th>
|
||||
<th>{% translate "Organization" %}</th>
|
||||
<th>{% translate "Group" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No group leave requests." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for leaverequest in leaverequests %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{ leaverequest.main_char|character_portrait_url:32 }}" class="img-circle" style="margin-right: 1rem;">
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|evewho_character_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.character_name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ leaverequest.user.username }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if leaverequest.main_char %}
|
||||
<a href="{{ leaverequest.main_char|dotlan_corporation_url }}" target="_blank">
|
||||
{{ leaverequest.main_char.corporation_name }}
|
||||
</a><br>
|
||||
{{ leaverequest.main_char.alliance_name|default_if_none:"" }}
|
||||
{% else %}
|
||||
{% translate "(unknown)" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ leaverequest.group.name }}</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url 'groupmanagement:leave_accept_request' leaverequest.id %}" class="btn btn-success">
|
||||
{% translate "Accept" %}
|
||||
</a>
|
||||
|
||||
<a href="{% url 'groupmanagement:leave_reject_request' leaverequest.id %}" class="btn btn-danger">
|
||||
{% translate "Reject" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% translate "No group leave requests." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.test import RequestFactory, TestCase
|
||||
from django.test import RequestFactory, TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
@ -6,14 +6,80 @@ from allianceauth.tests.auth_utils import AuthUtils
|
||||
from .. import views
|
||||
|
||||
|
||||
def response_content_to_str(response) -> str:
|
||||
return response.content.decode(response.charset)
|
||||
|
||||
|
||||
class TestViews(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
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):
|
||||
request = self.factory.get(reverse('groupmanagement:groups'))
|
||||
request.user = self.user
|
||||
response = views.groups_view(request)
|
||||
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(
|
||||
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)
|
||||
|
||||
|
@ -49,19 +49,22 @@ class NotificationManager(models.Manager):
|
||||
logger.info("Created notification %s", obj)
|
||||
return obj
|
||||
|
||||
def _max_notifications_per_user(self):
|
||||
"""return the maximum number of notifications allowed per user"""
|
||||
max_notifications = getattr(settings, 'NOTIFICATIONS_MAX_PER_USER', None)
|
||||
if (
|
||||
max_notifications is None
|
||||
or not isinstance(max_notifications, int)
|
||||
or max_notifications < 0
|
||||
):
|
||||
def _max_notifications_per_user(self) -> int:
|
||||
"""Maximum number of notifications allowed per user."""
|
||||
max_notifications = getattr(
|
||||
settings,
|
||||
"NOTIFICATIONS_MAX_PER_USER",
|
||||
self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||
)
|
||||
try:
|
||||
max_notifications = int(max_notifications)
|
||||
except ValueError:
|
||||
max_notifications = None
|
||||
if max_notifications is None or max_notifications < 0:
|
||||
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
|
||||
|
||||
return max_notifications
|
||||
|
||||
def user_unread_count(self, user_pk: int) -> int:
|
||||
|
@ -1,5 +1,6 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
@ -113,29 +114,53 @@ class TestUserNotify(TestCase):
|
||||
self.assertSetEqual(result, expected)
|
||||
|
||||
|
||||
@patch("allianceauth.notifications.managers.logger")
|
||||
@patch(
|
||||
MODULE_PATH + '.Notification.NOTIFICATIONS_MAX_PER_USER_DEFAULT',
|
||||
MODULE_PATH + ".Notification.NOTIFICATIONS_MAX_PER_USER_DEFAULT",
|
||||
NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||
)
|
||||
class TestMaxNotificationsPerUser(TestCase):
|
||||
|
||||
@override_settings(NOTIFICATIONS_MAX_PER_USER=None)
|
||||
def test_reset_to_default_if_not_defined(self):
|
||||
@override_settings(NOTIFICATIONS_MAX_PER_USER=42)
|
||||
def test_should_use_custom_integer_setting(self, mock_logger):
|
||||
# when
|
||||
result = Notification.objects._max_notifications_per_user()
|
||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||
self.assertEqual(result, expected)
|
||||
# then
|
||||
self.assertEqual(result, 42)
|
||||
self.assertFalse(mock_logger.warning.called)
|
||||
|
||||
@override_settings(NOTIFICATIONS_MAX_PER_USER='11')
|
||||
def test_reset_to_default_if_not_int(self):
|
||||
@override_settings(NOTIFICATIONS_MAX_PER_USER="42")
|
||||
def test_should_use_custom_string_setting(self, mock_logger):
|
||||
# when
|
||||
result = Notification.objects._max_notifications_per_user()
|
||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||
self.assertEqual(result, expected)
|
||||
# then
|
||||
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)
|
||||
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()
|
||||
expected = NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||
self.assertEqual(result, expected)
|
||||
# then
|
||||
self.assertEqual(result, NOTIFICATIONS_MAX_PER_USER_DEFAULT)
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
|
||||
|
||||
@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 -->
|
||||
<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" />
|
||||
<!-- End jQuery UI CSS from cdnjs -->
|
||||
{% load static %}
|
||||
<!-- Start jQuery UI CSS from Alliance Auth -->
|
||||
<!-- 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
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:8.2
|
||||
image: grafana/grafana-oss:8.2
|
||||
restart: always
|
||||
depends_on:
|
||||
- auth_mysql
|
||||
|
@ -90,11 +90,16 @@ This allows you to more finely control who has access to manage which groups.
|
||||
|
||||
### 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
|
||||
## 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
|
||||
|
3
tox.ini
3
tox.ini
@ -1,7 +1,7 @@
|
||||
[tox]
|
||||
skipsdist = true
|
||||
usedevelop = true
|
||||
envlist = py{38,39,310}-{all,core}
|
||||
envlist = py{38,39,310,311}-{all,core}
|
||||
|
||||
[testenv]
|
||||
setenv =
|
||||
@ -12,6 +12,7 @@ basepython =
|
||||
py38: python3.8
|
||||
py39: python3.9
|
||||
py310: python3.10
|
||||
py311: python3.11
|
||||
deps=
|
||||
coverage
|
||||
install_command = pip install -e ".[testing]" -U {opts} {packages}
|
||||
|
Loading…
x
Reference in New Issue
Block a user