Compare commits

...

67 Commits

Author SHA1 Message Date
Ariel Rin
b02827cb3f Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1699
2025-04-29 02:57:50 +00:00
Ariel Rin
2bcc0570ad Updates for project Alliance Auth 2025-04-29 02:57:50 +00:00
Ariel Rin
a3ea0c65a1 Merge branch 'timerboard-skyhooks' into 'master'
Add a new `theft` timer type for skyhooks

See merge request allianceauth/allianceauth!1708
2025-04-29 02:52:38 +00:00
Ariel Rin
5e526da11c Merge branch 'add-apply_offset-to-esi_cleanup_token-task' into 'master'
[ADD] `apply_offset` to `esi_cleanup_token` task

See merge request allianceauth/allianceauth!1709
2025-04-29 02:49:56 +00:00
Ariel Rin
5c79265f90 Merge branch 'services-grid' into 'master'
[FIX] Top margin for service controls

See merge request allianceauth/allianceauth!1711
2025-04-29 02:48:11 +00:00
Ariel Rin
eb0134e716 Merge branch 'menu-separator' into 'master'
[ADD] Menu separator

See merge request allianceauth/allianceauth!1712
2025-04-29 02:48:00 +00:00
Ariel Rin
afde1f4729 Merge branch 'typehint-object-managers' into 'master'
Typehint object managers

See merge request allianceauth/allianceauth!1713
2025-04-29 02:47:12 +00:00
Ariel Rin
5c6dda0eac Typehint object managers 2025-04-29 02:47:11 +00:00
Ariel Rin
af453bc772 Merge branch 'aa_i18n-addition' into 'master'
[ADD] Temaplatetag for path relative to `static`

See merge request allianceauth/allianceauth!1714
2025-04-29 02:46:56 +00:00
Ariel Rin
e13674e886 Merge branch 'translatable-app-names' into 'master'
[CHANGE] Made app names translatable in Django admin

See merge request allianceauth/allianceauth!1715
2025-04-29 02:45:48 +00:00
Ariel Rin
e3e856b826 Merge branch 'admin-overview' into 'master'
Remove unused {% if %} tag

See merge request allianceauth/allianceauth!1718
2025-04-29 02:42:35 +00:00
T'rahk Rokym
9d1cd23a8f Remove unused {% if %} tag 2025-04-21 16:50:04 +02:00
Peter Pfeufer
148f7c116f [CHANGE] Made app names translatable in Django admin 2025-04-10 07:08:16 +02:00
Peter Pfeufer
33e7134d6f [ADD] Temaplatetag for path relative to static
AA-GDPR needs this
2025-04-09 20:54:51 +02:00
Peter Pfeufer
fb799551aa [CHANGE] Move what ever is possible to our framework CSS 2025-04-06 22:22:54 +02:00
Peter Pfeufer
7b95051fe1 [ADD] Menu separator
Between `#nav-right` and `#nav-right-character-control`. Only visible when `#nav-right` has menu items.
2025-04-06 22:01:54 +02:00
Peter Pfeufer
efb6a6db4f [FIX] Top margin for service controls 2025-04-05 23:49:58 +02:00
Peter Pfeufer
478aa1aa12 [ADD] apply_offset to esi_cleanup_token task 2025-04-03 00:59:04 +02:00
T'rahk Rokym
751e55ed6c Add a new theft timer type for skyhooks 2025-03-26 22:09:43 +01:00
Ariel Rin
9dad53f763 Merge branch 'master' into 'master'
Switch the doc example to use group instead of chain for long running tasks

See merge request allianceauth/allianceauth!1703
2025-03-26 01:48:29 +00:00
Ariel Rin
2d57064a7a Merge branch 'supervisor-config-template-updates' into 'master'
[CHANGE] Update supervisor.conf template

See merge request allianceauth/allianceauth!1700
2025-03-26 01:47:58 +00:00
Ariel Rin
833d12cf66 Merge branch 'opengraph-template' into 'master'
[ADD] Opengraph Template

See merge request allianceauth/allianceauth!1701
2025-03-26 01:47:35 +00:00
Ariel Rin
7b56caa4cb Merge branch 'perms_fix' into 'master'
Use the mumble.view_connection_history perm not superuser for Mumble service statistics

See merge request allianceauth/allianceauth!1702
2025-03-26 01:47:12 +00:00
Ariel Rin
5752644122 Merge branch 'fix-logo-height' into 'master'
[FIX] `logo_height` variable name

See merge request allianceauth/allianceauth!1704
2025-03-26 01:46:12 +00:00
Ariel Rin
cadc0cb534 Merge branch 'page-header' into 'master'
[CHANGE] Wrap page header in `header` element

See merge request allianceauth/allianceauth!1705
2025-03-26 01:46:04 +00:00
Ariel Rin
dcdab5ae1f Merge branch 'fix-spelling' into 'master'
[FIX] Spelling (It's EVE time, not Eve time)

See merge request allianceauth/allianceauth!1706
2025-03-26 01:45:58 +00:00
Ariel Rin
d64e896288 Merge branch 'fix-scrollbar-positioning' into 'master'
[FIX] Scrollbar positioning in content area

See merge request allianceauth/allianceauth!1707
2025-03-26 01:45:38 +00:00
Peter Pfeufer
500d8ede32 [FIX] Scrollbar positioning in content area 2025-03-24 17:47:40 +01:00
Peter Pfeufer
f4c5c7f6db [FIX] Spelling (It's EVE time, not Eve time) 2025-03-24 15:14:30 +01:00
Peter Pfeufer
43e1be4032 [CHANGE] Move margin class to header element 2025-03-24 14:49:51 +01:00
Peter Pfeufer
702def2a4d [CHANGE] Wrap page header in header element 2025-03-24 13:34:11 +01:00
Peter Pfeufer
a34baf4154 [FIX] logo_height variable name 2025-03-22 17:37:10 +01:00
r0kym
4de0774f15 Switch the doc example to use group instead of chain for long running tasks 2025-03-20 18:47:19 +01:00
Aaron Kable
83d2dfc7d9 use the view_connection_history perm not superuser 2025-03-20 19:11:08 +08:00
Peter Pfeufer
c93afd2d68 [ADD] Site name and URL 2025-03-18 21:40:47 +01:00
Peter Pfeufer
b7bacd11af [CHANGE] Bring public base templkate in line with the non-public version 2025-03-18 18:30:00 +01:00
Peter Pfeufer
f26835fae0 [ADD] Opengraph Template for easier override 2025-03-18 18:29:22 +01:00
Peter Pfeufer
4edb7cb678 [CHANGE] Assignment format to INI style
The supervisor.conf file uses INI style, so the format should reflect this.
2025-03-16 10:15:36 +01:00
Peter Pfeufer
2ce9ba997f [CHANGE] Update supervisor.conf template
Use `%(program_name)s` variable for logfile names, which makes it easier to avoid possible copy/paste issues and mistakes.
2025-03-16 10:11:27 +01:00
Joel Falknau
055077fa77 Version Bump 4.6.4 2025-03-03 11:49:43 +10:00
Joel Falknau
f342ccbc6a Revert "[REMOVE] Unused imports"
This reverts commit f497c18e5b.
2025-03-03 11:47:19 +10:00
Joel Falknau
37ffd0a1ac Version Bump 4.6.3 2025-03-03 11:29:26 +10:00
Ariel Rin
a1f705381e Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1687
2025-03-03 01:28:36 +00:00
Ariel Rin
c0970ad4fa Updates for project Alliance Auth 2025-03-03 01:28:36 +00:00
Ariel Rin
3818d0c6d1 Merge branch 'crontab' into 'master'
Crontab

See merge request allianceauth/allianceauth!1695
2025-03-03 01:19:32 +00:00
Ariel Rin
95411c79cb Crontab 2025-03-03 01:19:32 +00:00
Ariel Rin
eeccbbacfc Merge branch 'limit-slixmpp' into 'master'
[CHANGE] Limit `slixmpp` version

See merge request allianceauth/allianceauth!1694
2025-03-03 01:19:09 +00:00
Peter Pfeufer
f6c4180502 [CHANGE] Limit slixmpp version
An update to `slixmpp` v1.9.0 pulls in `pyasn1` v0.6.1, which causes a dependency issue with `python-jose`

> ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
> python-jose 3.4.0 requires pyasn1<0.5.0,>=0.4.1, but you have pyasn1 0.6.1 which is incompatible.
2025-02-28 11:09:22 +01:00
Ariel Rin
f4d3d6c0b1 Merge branch 'sorting-data' into 'master'
[ADD] Sorting data to the tables datetime fields

See merge request allianceauth/allianceauth!1691
2025-02-27 03:37:30 +00:00
Ariel Rin
e9d2d11297 Merge branch 'correct-locale-link' into 'master'
[FIX] Correct locale link for moment js

See merge request allianceauth/allianceauth!1692
2025-02-27 03:37:27 +00:00
Peter Pfeufer
96204b29e8 [REMOVE] Redundant locale information
Since momentJS is already loaded with `locale=True`, this is not needed anymore.
2025-02-26 22:57:19 +01:00
Peter Pfeufer
47842c1243 [FIX] Correct locale link for moment js
The other one was the directory for the node modules …
2025-02-26 21:53:27 +01:00
Peter Pfeufer
9b494106bc [ADD] Sorting data to the table field
Since the localized date and time values can have different formats, sorting with them wasn't gonna work reliably, so we add the original datetime string as sorting data.
2025-02-26 21:33:21 +01:00
Ariel Rin
d51e730a7f Merge branch 'mumble-stats-date-fixes' into 'master'
[CHANGE] Datetime Improvements for Mumble Connection History

See merge request allianceauth/allianceauth!1689
2025-02-26 10:21:39 +00:00
Ariel Rin
363909c0c4 Merge branch 'remove-stray-a-tag' into 'master'
[REMOVE] Stray `a` tag from `header-nav-brand`

See merge request allianceauth/allianceauth!1688
2025-02-26 10:19:38 +00:00
Ariel Rin
82273f68fe Merge branch 'expand-checks' into 'master'
Expand checks

See merge request allianceauth/allianceauth!1685
2025-02-26 10:19:10 +00:00
Ariel Rin
12fa38b446 Merge branch 'lcode-links' into 'master'
[ADD] Links to available languages for mapping

See merge request allianceauth/allianceauth!1690
2025-02-26 10:18:31 +00:00
Peter Pfeufer
c26af593ff [ADD] Links to available languaes for mapping
Makes it easier for adding new languages in the future
2025-02-26 11:11:18 +01:00
Peter Pfeufer
8e9a53c494 [CHANGE] Localze datetime output 2025-02-25 18:17:51 +01:00
Peter Pfeufer
5559ce5fbb [CHANGE] Return empty field when no datetime available 2025-02-25 18:01:58 +01:00
Peter Pfeufer
faa529a55b [REMOVE] Stray a tag from header-nav-brand 2025-02-25 17:33:02 +01:00
Peter Pfeufer
4ccfe20c14 [CHANGE] Set email to dummy@example.net 2025-02-25 11:52:25 +01:00
Peter Pfeufer
3acb651650 [CHANGE] Update test 2025-02-19 10:18:35 +01:00
Peter Pfeufer
2de57b334b [ADD] ESI_USER_CONTACT_EMAIL to test settings 2025-02-19 10:09:00 +01:00
Peter Pfeufer
0498f5bb1b [MISC] Format for better readability 2025-02-19 10:00:18 +01:00
Peter Pfeufer
929485a8f9 [ADD] Check for empty SITE_URL 2025-02-19 09:39:22 +01:00
Peter Pfeufer
28cb62f373 [ADD] Check for ESI_USER_CONTACT_EMAIL 2025-02-19 09:31:49 +01:00
114 changed files with 3231 additions and 2726 deletions

View File

@@ -5,7 +5,7 @@ manage online service access.
# This will make sure the app is always imported when # This will make sure the app is always imported when
# Django starts so that shared_task will use this app. # Django starts so that shared_task will use this app.
__version__ = '4.6.2a' __version__ = '4.6.4'
__title__ = 'Alliance Auth' __title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth' __url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = f'{__title__} v{__version__}' NAME = f'{__title__} v{__version__}'

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class AnalyticsConfig(AppConfig): class AnalyticsConfig(AppConfig):
name = 'allianceauth.analytics' name = 'allianceauth.analytics'
label = 'analytics' label = 'analytics'
verbose_name = _('Analytics')

View File

@@ -1,6 +1,7 @@
from django.apps import apps from django.apps import apps
from allianceauth.authentication.models import User from allianceauth.authentication.models import User
from allianceauth.analytics.utils import install_stat_users, install_stat_addons from esi.models import Token
from allianceauth.analytics.utils import install_stat_users, install_stat_tokens, install_stat_addons
from django.test.testcases import TestCase from django.test.testcases import TestCase

View File

@@ -1,10 +1,12 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.core.checks import register, Tags from django.core.checks import register, Tags
from django.utils.translation import gettext_lazy as _
class AuthenticationConfig(AppConfig): class AuthenticationConfig(AppConfig):
name = "allianceauth.authentication" name = "allianceauth.authentication"
label = "authentication" label = "authentication"
verbose_name = _("Authentication")
def ready(self): def ready(self):
from allianceauth.authentication import checks, signals # noqa: F401 from allianceauth.authentication import checks, signals # noqa: F401

View File

@@ -1,3 +1,6 @@
from django.urls import include
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
from functools import wraps from functools import wraps
from typing import Callable, Iterable, Optional from typing import Callable, Iterable, Optional

View File

@@ -1,10 +1,12 @@
import logging import logging
from typing import ClassVar
from django.contrib.auth.models import User, Permission from django.contrib.auth.models import User, Permission
from django.db import models, transaction from django.db import models, transaction
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
from allianceauth.notifications import notify from allianceauth.notifications import notify
from django.conf import settings
from .managers import CharacterOwnershipManager, StateManager from .managers import CharacterOwnershipManager, StateManager
@@ -26,7 +28,7 @@ class State(models.Model):
help_text="Factions to whose members this state is available.") help_text="Factions to whose members this state is available.")
public = models.BooleanField(default=False, help_text="Make this state available to any character.") public = models.BooleanField(default=False, help_text="Make this state available to any character.")
objects = StateManager() objects: ClassVar[StateManager] = StateManager()
class Meta: class Meta:
ordering = ['-priority'] ordering = ['-priority']
@@ -136,8 +138,10 @@ class UserProfile(models.Model):
sender=self.__class__, user=self.user, state=self.state sender=self.__class__, user=self.user, state=self.state
) )
def __str__(self): def __str__(self) -> str:
return str(self.user) return str(self.user)
class CharacterOwnership(models.Model): class CharacterOwnership(models.Model):
class Meta: class Meta:
default_permissions = ('change', 'delete') default_permissions = ('change', 'delete')
@@ -147,7 +151,7 @@ class CharacterOwnership(models.Model):
owner_hash = models.CharField(max_length=28, unique=True) owner_hash = models.CharField(max_length=28, unique=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='character_ownerships') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='character_ownerships')
objects = CharacterOwnershipManager() objects: ClassVar[CharacterOwnershipManager] = CharacterOwnershipManager()
def __str__(self): def __str__(self):
return f"{self.user}: {self.character}" return f"{self.user}: {self.character}"

View File

@@ -1,24 +1,24 @@
{% load theme_tags %} {% load theme_tags %}
{% load static %} {% load static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" {% theme_html_tags %}>
<head> <head>
<!-- Required meta tags -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content=""> <!-- End Required meta tags -->
<meta name="author" content="">
<!-- TODO Bundle all the site specific stuff up into its own template for easy override -->
<meta property="og:title" content="{{ SITE_NAME }}">
<meta property="og:image" content="{{ SITE_URL }}{% static 'allianceauth/icons/apple-touch-icon.png' %}">
<meta property="og:description" content="Alliance Auth - An auth system for EVE Online to help in-game organizations manage online service access.">
<!-- Meta tags -->
{% include 'allianceauth/opengraph.html' %}
{% include 'allianceauth/icons.html' %} {% include 'allianceauth/icons.html' %}
<!-- Meta tags -->
<title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title> <title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title>
{% theme_css %} {% theme_css %}
{% include 'bundles/fontawesome.html' %} {% include 'bundles/fontawesome.html' %}
{% include 'bundles/auth-framework-css.html' %}
{% block extra_include %} {% block extra_include %}
{% endblock %} {% endblock %}

View File

@@ -1,3 +1,9 @@
from django.db.models.signals import (
m2m_changed,
post_save,
pre_delete,
pre_save
)
from django.urls import reverse from django.urls import reverse
from unittest import mock from unittest import mock

View File

@@ -1,3 +1,4 @@
from unittest import mock
from allianceauth.authentication.middleware import UserSettingsMiddleware from allianceauth.authentication.middleware import UserSettingsMiddleware
from unittest.mock import Mock from unittest.mock import Mock
from django.http import HttpResponse from django.http import HttpResponse

View File

@@ -4,10 +4,16 @@ from allianceauth.eveonline.models import (
EveCorporationInfo, EveCorporationInfo,
EveAllianceInfo EveAllianceInfo
) )
from django.db.models.signals import post_save from django.db.models.signals import (
pre_save,
post_save,
pre_delete,
m2m_changed
)
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from django.test.testcases import TestCase from django.test.testcases import TestCase
from unittest.mock import Mock
from . import patch from . import patch

View File

@@ -11,6 +11,7 @@ from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User
from django.core import signing from django.core import signing
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import redirect, render from django.shortcuts import redirect, render

View File

@@ -1,3 +1,7 @@
"""
Django system checks for Alliance Auth
"""
from typing import List from typing import List
from django import db from django import db
from django.core.checks import CheckMessage, Error, register, Warning from django.core.checks import CheckMessage, Error, register, Warning
@@ -16,48 +20,172 @@ B = Configuration
@register() @register()
def django_settings(app_configs, **kwargs) -> List[CheckMessage]: def django_settings(app_configs, **kwargs) -> List[CheckMessage]:
errors: List[CheckMessage] = [] """
if hasattr(settings, "SITE_URL"): Check that Django settings are correctly configured
if settings.SITE_URL[-1] == "/":
errors.append(Warning("'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth.", hint="", id="allianceauth.checks.B005"))
else:
errors.append(Error("No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.", hint="", id="allianceauth.checks.B006"))
if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"): :param app_configs:
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS: :type app_configs:
errors.append(Warning("'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.", hint="", id="allianceauth.checks.B007")) :param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = []
# Check for SITE_URL
if hasattr(settings, "SITE_URL"):
# Check if SITE_URL is empty
if settings.SITE_URL == "":
errors.append(
Error(
msg="'SITE_URL' is empty.",
hint="Make sure to set 'SITE_URL' to the URL of your Auth instance. (Without trailing slash)",
id="allianceauth.checks.B011",
)
)
# Check if SITE_URL has a trailing slash
elif settings.SITE_URL[-1] == "/":
errors.append(
Warning(
msg="'SITE_URL' has a trailing slash. This may lead to incorrect links being generated by Auth.",
hint="",
id="allianceauth.checks.B005",
)
)
# SITE_URL not found
else: else:
errors.append(Error("No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified", hint="", id="allianceauth.checks.B008")) errors.append(
Error(
msg="No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.",
hint="",
id="allianceauth.checks.B006",
)
)
# Check for CSRF_TRUSTED_ORIGINS
if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"):
# Check if SITE_URL is not in CSRF_TRUSTED_ORIGINS
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS:
errors.append(
Warning(
msg="'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.",
hint="",
id="allianceauth.checks.B007",
)
)
# CSRF_TRUSTED_ORIGINS not found
else:
errors.append(
Error(
msg="No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified",
hint="",
id="allianceauth.checks.B008",
)
)
# Check for ESI_USER_CONTACT_EMAIL
if hasattr(settings, "ESI_USER_CONTACT_EMAIL"):
# Check if ESI_USER_CONTACT_EMAIL is empty
if settings.ESI_USER_CONTACT_EMAIL == "":
errors.append(
Error(
msg="'ESI_USER_CONTACT_EMAIL' is empty. A valid email is required as maintainer contact for CCP.",
hint="",
id="allianceauth.checks.B009",
)
)
# ESI_USER_CONTACT_EMAIL not found
else:
errors.append(
Error(
msg="No 'ESI_USER_CONTACT_EMAIL' found is settings. A valid email is required as maintainer contact for CCP.",
hint="",
id="allianceauth.checks.B010",
)
)
return errors return errors
@register() @register()
def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]: def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that Redis is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
allianceauth_redis_install_link = "https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools" allianceauth_redis_install_link = "https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools"
errors: List[CheckMessage] = [] errors: List[CheckMessage] = []
try: try:
redis_version = Pep440Version(get_redis_client().info()['redis_version']) redis_version = Pep440Version(get_redis_client().info()["redis_version"])
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm Redis Version")) errors.append(Warning("Unable to confirm Redis Version"))
return errors return errors
if redis_version.major == 7 and redis_version.minor == 2 and timezone.now() > timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc): if (
errors.append(Error(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A001")) redis_version.major == 7
and redis_version.minor == 2
and timezone.now()
> timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc)
):
errors.append(
Error(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A001",
)
)
elif redis_version.major == 7 and redis_version.minor == 0: elif redis_version.major == 7 and redis_version.minor == 0:
errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A002")) errors.append(
Warning(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A002",
)
)
elif redis_version.major == 6 and redis_version.minor == 2: elif redis_version.major == 6 and redis_version.minor == 2:
errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A018")) errors.append(
Warning(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A018",
)
)
elif redis_version.major in [6, 5]: elif redis_version.major in [6, 5]:
errors.append(Error(f"Redis {redis_version.public} EOL", hint=allianceauth_redis_install_link, id="allianceauth.checks.A003")) errors.append(
Error(
msg=f"Redis {redis_version.public} EOL",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A003",
)
)
return errors return errors
@register() @register()
def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]: def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that MySQL is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
mysql_quick_guide_link = "https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/" mysql_quick_guide_link = "https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/"
errors: List[CheckMessage] = [] errors: List[CheckMessage] = []
@@ -65,108 +193,302 @@ def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "mysql": if connection.vendor == "mysql":
try: try:
mysql_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) mysql_version = Pep440Version(
".".join(str(i) for i in connection.mysql_version)
)
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm MySQL Version")) errors.append(Warning("Unable to confirm MySQL Version"))
return errors return errors
# MySQL 8 # MySQL 8
if mysql_version.major == 8: if mysql_version.major == 8:
if mysql_version.minor == 4 and timezone.now() > timezone.datetime(year=2032, month=4, day=30, tzinfo=timezone.utc): if mysql_version.minor == 4 and timezone.now() > timezone.datetime(
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A004")) year=2032, month=4, day=30, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A004",
)
)
elif mysql_version.minor == 3: elif mysql_version.minor == 3:
errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint=mysql_quick_guide_link, id="allianceauth.checks.A005")) errors.append(
Warning(
msg=f"MySQL {mysql_version.public} Non LTS",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A005",
)
)
elif mysql_version.minor == 2: elif mysql_version.minor == 2:
errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint=mysql_quick_guide_link, id="allianceauth.checks.A006")) errors.append(
Warning(
msg=f"MySQL {mysql_version.public} Non LTS",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A006",
)
)
elif mysql_version.minor == 1: elif mysql_version.minor == 1:
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A007")) errors.append(
elif mysql_version.minor == 0 and timezone.now() > timezone.datetime(year=2026, month=4, day=30, tzinfo=timezone.utc): Error(
errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A008")) msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A007",
)
)
elif mysql_version.minor == 0 and timezone.now() > timezone.datetime(
year=2026, month=4, day=30, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A008",
)
)
# MySQL below 8 # MySQL below 8
# This will also catch Mariadb 5.x # This will also catch Mariadb 5.x
elif mysql_version.major < 8: elif mysql_version.major < 8:
errors.append(Error(f"MySQL or MariaDB {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A009")) errors.append(
Error(
msg=f"MySQL or MariaDB {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A009",
)
)
return errors return errors
@register() @register()
def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]: def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that MariaDB is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
mariadb_download_link = "https://mariadb.org/download/?t=repo-config" mariadb_download_link = "https://mariadb.org/download/?t=repo-config"
errors: List[CheckMessage] = [] errors: List[CheckMessage] = []
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "mysql": # Still to find a way to determine MySQL vs MariaDB # TODO: Find a way to determine MySQL vs. MariaDB
if connection.vendor == "mysql":
try: try:
mariadb_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) mariadb_version = Pep440Version(
".".join(str(i) for i in connection.mysql_version)
)
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm MariaDB Version")) errors.append(Warning("Unable to confirm MariaDB Version"))
return errors return errors
# MariaDB 11 # MariaDB 11
if mariadb_version.major == 11: if mariadb_version.major == 11:
if mariadb_version.minor == 4 and timezone.now() > timezone.datetime(year=2029, month=5, day=19, tzinfo=timezone.utc): if mariadb_version.minor == 4 and timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A010")) year=2029, month=5, day=19, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A010",
)
)
elif mariadb_version.minor == 2: elif mariadb_version.minor == 2:
errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint=mariadb_download_link, id="allianceauth.checks.A018")) errors.append(
Warning(
msg=f"MariaDB {mariadb_version.public} Non LTS",
hint=mariadb_download_link,
id="allianceauth.checks.A018",
)
)
if timezone.now() > timezone.datetime(year=2024, month=11, day=21, tzinfo=timezone.utc): if timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A011")) year=2024, month=11, day=21, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A011",
)
)
elif mariadb_version.minor == 1: elif mariadb_version.minor == 1:
errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint=mariadb_download_link, id="allianceauth.checks.A019")) errors.append(
Warning(
msg=f"MariaDB {mariadb_version.public} Non LTS",
hint=mariadb_download_link,
id="allianceauth.checks.A019",
)
)
if timezone.now() > timezone.datetime(year=2024, month=8, day=21, tzinfo=timezone.utc): if timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A012")) year=2024, month=8, day=21, tzinfo=timezone.utc
elif mariadb_version.minor in [0, 3]: # Demote versions down here once EOL ):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A013")) errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A012",
)
)
# Demote versions down here once EOL
elif mariadb_version.minor in [0, 3]:
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A013",
)
)
# MariaDB 10 # MariaDB 10
elif mariadb_version.major == 10: elif mariadb_version.major == 10:
if mariadb_version.minor == 11 and timezone.now() > timezone.datetime(year=2028, month=2, day=10, tzinfo=timezone.utc): if mariadb_version.minor == 11 and timezone.now() > timezone.datetime(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A014")) year=2028, month=2, day=10, tzinfo=timezone.utc
elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime(year=2026, month=7, day=6, tzinfo=timezone.utc): ):
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A0015")) errors.append(
elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime(year=2025, month=6, day=24, tzinfo=timezone.utc): Error(
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A016")) msg=f"MariaDB {mariadb_version.public} EOL",
elif mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]: # Demote versions down here once EOL hint=mariadb_download_link,
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A017")) id="allianceauth.checks.A014",
)
)
elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime(
year=2026, month=7, day=6, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A0015",
)
)
elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime(
year=2025, month=6, day=24, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A016",
)
)
# Demote versions down here once EOL
elif mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]:
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A017",
)
)
return errors return errors
@register() @register()
def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]: def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that SQLite is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = [] errors: List[CheckMessage] = []
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "sqlite": if connection.vendor == "sqlite":
try: try:
sqlite_version = Pep440Version(".".join(str(i) for i in sqlite_version_info)) sqlite_version = Pep440Version(
".".join(str(i) for i in sqlite_version_info)
)
except InvalidVersion: except InvalidVersion:
errors.append(Warning("Unable to confirm SQLite Version")) errors.append(Warning("Unable to confirm SQLite Version"))
return errors return errors
if sqlite_version.major == 3 and sqlite_version.minor < 27: if sqlite_version.major == 3 and sqlite_version.minor < 27:
errors.append(Error(f"SQLite {sqlite_version.public} Unsupported by Django", hint="https://pkgs.org/download/sqlite3", id="allianceauth.checks.A020")) errors.append(
Error(
msg=f"SQLite {sqlite_version.public} Unsupported by Django",
hint="https://pkgs.org/download/sqlite3",
id="allianceauth.checks.A020",
)
)
return errors return errors
@register() @register()
def sql_settings(app_configs, **kwargs) -> List[CheckMessage]: def sql_settings(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that SQL settings are correctly configured
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = [] errors: List[CheckMessage] = []
for connection in db.connections.all(): for connection in db.connections.all():
if connection.vendor == "mysql": if connection.vendor == "mysql":
try: try:
if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4": if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4":
errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) errors.append(
Error(
msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
id="allianceauth.checks.B001",
)
)
except KeyError: except KeyError:
errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) errors.append(
Error(
msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
id="allianceauth.checks.B001",
)
)
# This hasn't actually been set on AA yet # This hasn't actually been set on AA yet
# try: # try:
# if connection.settings_dict["OPTIONS"]["collation"] != "utf8mb4_unicode_ci": # if (
# errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) # connection.settings_dict["OPTIONS"]["collation"]
# != "utf8mb4_unicode_ci"
# ):
# errors.append(
# Error(
# msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}",
# hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
# id="allianceauth.checks.B001",
# )
# )
# except KeyError: # except KeyError:
# errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) # errors.append(
# Error(
# msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}",
# hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
# id="allianceauth.checks.B001",
# )
# )
# if connection.vendor == "sqlite": # if connection.vendor == "sqlite":
@@ -175,19 +497,57 @@ def sql_settings(app_configs, **kwargs) -> List[CheckMessage]:
@register() @register()
def celery_settings(app_configs, **kwargs) -> List[CheckMessage]: def celery_settings(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that Celery settings are correctly configured
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = [] errors: List[CheckMessage] = []
try: try:
if current_app.conf.broker_transport_options != {'priority_steps': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'queue_order_strategy': 'priority'}: if current_app.conf.broker_transport_options != {
errors.append(Error("Celery Priorities are not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003")) "priority_steps": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"queue_order_strategy": "priority",
}:
errors.append(
Error(
msg="Celery Priorities are not set correctly",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc",
id="allianceauth.checks.B003",
)
)
except KeyError: except KeyError:
errors.append(Error("Celery Priorities are not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003")) errors.append(
Error(
msg="Celery Priorities are not set",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc",
id="allianceauth.checks.B003",
)
)
try: try:
if current_app.conf.broker_connection_retry_on_startup != True: if not current_app.conf.broker_connection_retry_on_startup:
errors.append(Error("Celery broker_connection_retry_on_startup not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004")) errors.append(
Error(
msg="Celery broker_connection_retry_on_startup not set correctly",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280",
id="allianceauth.checks.B004",
)
)
except KeyError: except KeyError:
errors.append(Error("Celery broker_connection_retry_on_startup not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004")) errors.append(
Error(
msg="Celery broker_connection_retry_on_startup not set",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280",
id="allianceauth.checks.B004",
)
)
return errors return errors

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class CorpUtilsConfig(AppConfig): class CorpUtilsConfig(AppConfig):
name = 'allianceauth.corputils' name = 'allianceauth.corputils'
label = 'corputils' label = 'corputils'
verbose_name = _('Corporation Stats')

View File

@@ -1,5 +1,6 @@
import logging import logging
import os import os
from typing import ClassVar
from allianceauth.authentication.models import CharacterOwnership, UserProfile from allianceauth.authentication.models import CharacterOwnership, UserProfile
from bravado.exception import HTTPForbidden from bravado.exception import HTTPForbidden
@@ -40,9 +41,9 @@ class CorpStats(models.Model):
verbose_name = "corp stats" verbose_name = "corp stats"
verbose_name_plural = "corp stats" verbose_name_plural = "corp stats"
objects = CorpStatsManager() objects: ClassVar[CorpStatsManager] = CorpStatsManager()
def __str__(self): def __str__(self) -> str:
return f"{self.__class__.__name__} for {self.corp}" return f"{self.__class__.__name__} for {self.corp}"
def update(self): def update(self):

View File

@@ -3,6 +3,7 @@ Crontab App Config
""" """
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class CrontabConfig(AppConfig): class CrontabConfig(AppConfig):
@@ -12,3 +13,4 @@ class CrontabConfig(AppConfig):
name = "allianceauth.crontab" name = "allianceauth.crontab"
label = "crontab" label = "crontab"
verbose_name = _("Crontab")

View File

@@ -40,10 +40,11 @@ class OffsetDatabaseScheduler(DatabaseScheduler):
for name, entry_fields in mapping.items(): for name, entry_fields in mapping.items():
try: try:
apply_offset = entry_fields.pop("apply_offset", False) # Ensure this pops before django tries to save to ORM apply_offset = entry_fields.pop("apply_offset", False) # Ensure this pops before django tries to save to ORM
entry = self.Entry.from_entry(name, app=self.app, **entry_fields) entry = self.Entry.from_entry(name, app=self.app, **entry_fields)
if apply_offset: if apply_offset:
entry_fields.update({"apply_offset": apply_offset}) # Reapply this as its gets pulled from config inconsistently.
schedule_obj = entry.schedule schedule_obj = entry.schedule
if isinstance(schedule_obj, schedules.crontab): if isinstance(schedule_obj, schedules.crontab):
offset_cs = CrontabSchedule.from_schedule(offset_cron(schedule_obj)) offset_cs = CrontabSchedule.from_schedule(offset_cron(schedule_obj))
@@ -55,6 +56,7 @@ class OffsetDatabaseScheduler(DatabaseScheduler):
day_of_week=offset_cs.day_of_week, day_of_week=offset_cs.day_of_week,
timezone=offset_cs.timezone, timezone=offset_cs.timezone,
) )
entry.schedule = offset_cron(schedule_obj) # This gets passed into Celery Beats Memory, important to keep it in sync with the model/DB
entry.model.crontab = offset_cs entry.model.crontab = offset_cs
entry.model.save() entry.model.save()
logger.debug(f"Offset applied for '{name}' due to 'apply_offset' = True.") logger.debug(f"Offset applied for '{name}' due to 'apply_offset' = True.")

View File

@@ -1,3 +1,5 @@
# myapp/tests/test_tasks.py
import logging import logging
from unittest.mock import patch from unittest.mock import patch
from django.test import TestCase from django.test import TestCase

View File

@@ -5,6 +5,9 @@ Form widgets for custom_css app
# Django # Django
from django import forms from django import forms
# Alliance Auth
from allianceauth.custom_css.models import CustomCSS
class CssEditorWidget(forms.Textarea): class CssEditorWidget(forms.Textarea):
""" """

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class EveonlineConfig(AppConfig): class EveonlineConfig(AppConfig):
name = 'allianceauth.eveonline' name = 'allianceauth.eveonline'
label = 'eveonline' label = 'eveonline'
verbose_name = _('EVE Online')

View File

@@ -1,9 +1,11 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class EveAutogroupsConfig(AppConfig): class EveAutogroupsConfig(AppConfig):
name = 'allianceauth.eveonline.autogroups' name = 'allianceauth.eveonline.autogroups'
label = 'eve_autogroups' label = 'eve_autogroups'
verbose_name = _('EVE Online Autogroups')
def ready(self): def ready(self):
import allianceauth.eveonline.autogroups.signals import allianceauth.eveonline.autogroups.signals

View File

@@ -1,4 +1,5 @@
import logging import logging
from typing import ClassVar
from django.db import models, transaction from django.db import models, transaction
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@@ -78,7 +79,7 @@ class AutogroupsConfig(models.Model):
max_length=10, default='', blank=True, max_length=10, default='', blank=True,
help_text='Any spaces in the group name will be replaced with this.') help_text='Any spaces in the group name will be replaced with this.')
objects = AutogroupsConfigManager() objects: ClassVar[AutogroupsConfigManager] = AutogroupsConfigManager()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@@ -1,5 +1,6 @@
from django.test import TestCase from django.test import TestCase
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.db import transaction
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils

View File

@@ -1,6 +1,8 @@
from django.test import TestCase from django.test import TestCase
from ...models import EveCharacter, EveCorporationInfo, EveAllianceInfo
from .. import dotlan, zkillboard, evewho, eveimageserver from .. import dotlan, zkillboard, evewho, eveimageserver
from ...templatetags import evelinks
class TestEveWho(TestCase): class TestEveWho(TestCase):

View File

@@ -14,10 +14,10 @@ class EveCharacterProviderManager:
class EveCharacterManager(models.Manager): class EveCharacterManager(models.Manager):
provider = EveCharacterProviderManager() provider = EveCharacterProviderManager()
def create_character(self, character_id): def create_character(self, character_id) -> models.Model:
return self.create_character_obj(self.provider.get_character(character_id)) return self.create_character_obj(self.provider.get_character(character_id))
def create_character_obj(self, character: providers.Character): def create_character_obj(self, character: providers.Character) -> models.Model:
return self.create( return self.create(
character_id=character.id, character_id=character.id,
character_name=character.name, character_name=character.name,

View File

@@ -1,5 +1,5 @@
import logging import logging
from typing import Union from typing import ClassVar, Union
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
@@ -75,8 +75,8 @@ class EveAllianceInfo(models.Model):
alliance_ticker = models.CharField(max_length=254) alliance_ticker = models.CharField(max_length=254)
executor_corp_id = models.PositiveIntegerField() executor_corp_id = models.PositiveIntegerField()
objects = EveAllianceManager() objects: ClassVar[EveAllianceManager] = EveAllianceManager()
provider = EveAllianceProviderManager() provider: ClassVar[EveAllianceProviderManager] = EveAllianceProviderManager()
class Meta: class Meta:
indexes = [models.Index(fields=['executor_corp_id',])] indexes = [models.Index(fields=['executor_corp_id',])]
@@ -147,7 +147,7 @@ class EveCorporationInfo(models.Model):
EveAllianceInfo, blank=True, null=True, on_delete=models.SET_NULL EveAllianceInfo, blank=True, null=True, on_delete=models.SET_NULL
) )
objects = EveCorporationManager() objects: ClassVar[EveCorporationManager] = EveCorporationManager()
provider = EveCorporationProviderManager() provider = EveCorporationProviderManager()
class Meta: class Meta:
@@ -214,7 +214,7 @@ class EveCharacter(models.Model):
faction_id = models.PositiveIntegerField(blank=True, null=True, default=None) faction_id = models.PositiveIntegerField(blank=True, null=True, default=None)
faction_name = models.CharField(max_length=254, blank=True, null=True, default='') faction_name = models.CharField(max_length=254, blank=True, null=True, default='')
objects = EveCharacterManager() objects: ClassVar[EveCharacterManager] = EveCharacterManager()
provider = EveCharacterProviderManager() provider = EveCharacterProviderManager()
class Meta: class Meta:

View File

@@ -723,5 +723,5 @@ class TestEveSwaggerProvider(TestCase):
my_client = my_provider.client my_client = my_provider.client
operation = my_client.Universe.get_universe_factions() operation = my_client.Universe.get_universe_factions()
self.assertEqual( self.assertEqual(
operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0' operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0 dummy@example.net'
) )

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class FatConfig(AppConfig): class FatConfig(AppConfig):
name = 'allianceauth.fleetactivitytracking' name = 'allianceauth.fleetactivitytracking'
label = 'fleetactivitytracking' label = 'fleetactivitytracking'
verbose_name = _('Fleet Activity Tracking')

View File

@@ -36,7 +36,7 @@
<th class="text-center">{% translate "Character" %}</th> <th class="text-center">{% translate "Character" %}</th>
<th class="text-center">{% translate "System" %}</th> <th class="text-center">{% translate "System" %}</th>
<th class="text-center">{% translate "Ship" %}</th> <th class="text-center">{% translate "Ship" %}</th>
<th class="text-center">{% translate "Eve Time" %}</th> <th class="text-center">{% translate "EVE time" %}</th>
<th></th> <th></th>
</tr> </tr>

View File

@@ -72,7 +72,7 @@
<tr> <tr>
<th class="text-center">{% translate "Fleet" %}</th> <th class="text-center">{% translate "Fleet" %}</th>
<th class="text-center">{% translate "Creator" %}</th> <th class="text-center">{% translate "Creator" %}</th>
<th class="text-center">{% translate "Eve Time" %}</th> <th class="text-center">{% translate "EVE time" %}</th>
<th class="text-center">{% translate "Duration" %}</th> <th class="text-center">{% translate "Duration" %}</th>
<th class="text-center">{% translate "Edit" %}</th> <th class="text-center">{% translate "Edit" %}</th>
</tr> </tr>

View File

@@ -41,7 +41,7 @@
<th scope="col" class="text-center">{% translate "Character" %}</th> <th scope="col" class="text-center">{% translate "Character" %}</th>
<th scope="col" class="text-center">{% translate "System" %}</th> <th scope="col" class="text-center">{% translate "System" %}</th>
<th scope="col" class="text-center">{% translate "Ship" %}</th> <th scope="col" class="text-center">{% translate "Ship" %}</th>
<th scope="col" class="text-center">{% translate "Eve Time" %}</th> <th scope="col" class="text-center">{% translate "EVE time" %}</th>
</tr> </tr>
{% for fat in fats %} {% for fat in fats %}
@@ -89,7 +89,7 @@
<th scope="col" class="text-center">{% translate "Name" %}</th> <th scope="col" class="text-center">{% translate "Name" %}</th>
<th scope="col" class="text-center">{% translate "Creator" %}</th> <th scope="col" class="text-center">{% translate "Creator" %}</th>
<th scope="col" class="text-center">{% translate "Fleet" %}</th> <th scope="col" class="text-center">{% translate "Fleet" %}</th>
<th scope="col" class="text-center">{% translate "Eve Time" %}</th> <th scope="col" class="text-center">{% translate "EVE time" %}</th>
<th scope="col" class="text-center">{% translate "Duration" %}</th> <th scope="col" class="text-center">{% translate "Duration" %}</th>
<th scope="col" class="text-center">{% translate "Edit" %}</th> <th scope="col" class="text-center">{% translate "Edit" %}</th>
</tr> </tr>

View File

@@ -8,7 +8,7 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404, Http404
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from esi.decorators import token_required from esi.decorators import token_required

View File

@@ -3,6 +3,7 @@ Framework App Config
""" """
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class FrameworkConfig(AppConfig): class FrameworkConfig(AppConfig):
@@ -12,3 +13,4 @@ class FrameworkConfig(AppConfig):
name = "allianceauth.framework" name = "allianceauth.framework"
label = "framework" label = "framework"
verbose_name = _("Framework")

View File

@@ -5,6 +5,24 @@
* to be used throughout Alliance Auth and its Community Apps * to be used throughout Alliance Auth and its Community Apps
*/ */
/* General
------------------------------------------------------------------------------------- */
@media all {
.navbar-toggler.collapsed {
transform: rotate(180deg);
}
ul#nav-right:has(li) + ul#nav-right-character-control > li:first-child {
display: list-item !important;
}
}
@media all and (max-width: 991px) {
ul#nav-left:has(li) + ul#nav-right + ul#nav-right-character-control > li:first-child {
display: list-item !important;
}
}
/* Bootstrap fixes /* Bootstrap fixes
------------------------------------------------------------------------------------- */ ------------------------------------------------------------------------------------- */
@media all { @media all {

View File

@@ -2,12 +2,14 @@
{# {% include "framework/header/page-header.html" with title="Foobar" subtitle="Barfoo" %}#} {# {% include "framework/header/page-header.html" with title="Foobar" subtitle="Barfoo" %}#}
{% if title %} {% if title %}
<h1 class="page-header text-center mb-3"> <header class="aa-page-header mb-3">
{{ title }} <h1 class="page-header text-center">
{{ title }}
{% if subtitle %} {% if subtitle %}
<br> <br>
<small class="text-muted">{{ subtitle }}</small> <small class="text-muted">{{ subtitle }}</small>
{% endif %} {% endif %}
</h1> </h1>
</header>
{% endif %} {% endif %}

View File

@@ -1,10 +1,11 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class GroupManagementConfig(AppConfig): class GroupManagementConfig(AppConfig):
name = 'allianceauth.groupmanagement' name = 'allianceauth.groupmanagement'
label = 'groupmanagement' label = 'groupmanagement'
verbose_name = 'Group Management' verbose_name = _('Group Management')
def ready(self): def ready(self):
from . import signals # noqa: F401 from . import signals # noqa: F401

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class HRApplicationsConfig(AppConfig): class HRApplicationsConfig(AppConfig):
name = 'allianceauth.hrapplications' name = 'allianceauth.hrapplications'
label = 'hrapplications' label = 'hrapplications'
verbose_name = _('HR Applications')

View File

@@ -1,3 +1,4 @@
from typing import ClassVar
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from sortedm2m.fields import SortedManyToManyField from sortedm2m.fields import SortedManyToManyField
@@ -40,7 +41,7 @@ class Application(models.Model):
reviewer_character = models.ForeignKey(EveCharacter, on_delete=models.SET_NULL, blank=True, null=True) reviewer_character = models.ForeignKey(EveCharacter, on_delete=models.SET_NULL, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
objects = ApplicationManager() objects: ClassVar[ApplicationManager] = ApplicationManager()
def __str__(self): def __str__(self):
return str(self.user) + " Application To " + str(self.form) return str(self.user) + " Application To " + str(self.form)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
import logging import logging
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -12,6 +13,7 @@ logger = logging.getLogger(__name__)
class MenuConfig(AppConfig): class MenuConfig(AppConfig):
name = "allianceauth.menu" name = "allianceauth.menu"
label = "menu" label = "menu"
verbose_name = _("Menu")
def ready(self): def ready(self):
from allianceauth.menu.core import smart_sync from allianceauth.menu.core import smart_sync

View File

@@ -1,3 +1,4 @@
from typing import ClassVar
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@@ -68,7 +69,7 @@ class MenuItem(models.Model):
help_text=_("External URL this menu items will link to"), help_text=_("External URL this menu items will link to"),
) )
objects = MenuItemManager() objects: ClassVar[MenuItemManager] = MenuItemManager()
def __str__(self) -> str: def __str__(self) -> str:
return self.text return self.text

View File

@@ -186,7 +186,7 @@ class TestRenderDefaultMenu(TestCase):
classes = "fa-solid fa-users-gear" classes = "fa-solid fa-users-gear"
url_name = "groupmanagement:management" url_name = "groupmanagement:management"
def render(self, request): def render(Self, request):
# simulate no perms # simulate no perms
return "" return ""

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class NotificationsConfig(AppConfig): class NotificationsConfig(AppConfig):
name = 'allianceauth.notifications' name = 'allianceauth.notifications'
label = 'notifications' label = 'notifications'
verbose_name = _('Notifications')

View File

@@ -1,4 +1,5 @@
import logging import logging
from typing import ClassVar
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
@@ -56,7 +57,7 @@ class Notification(models.Model):
timestamp = models.DateTimeField(auto_now_add=True, db_index=True) timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
viewed = models.BooleanField(default=False, db_index=True) viewed = models.BooleanField(default=False, db_index=True)
objects = NotificationManager() objects: ClassVar[NotificationManager] = NotificationManager()
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.user}: {self.title}" return f"{self.user}: {self.title}"

View File

@@ -1,4 +1,4 @@
from unittest.mock import patch from unittest.mock import patch, Mock
from django.test import TestCase, override_settings from django.test import TestCase, override_settings

View File

@@ -1,6 +1,6 @@
import json import json
from unittest.mock import patch from unittest.mock import patch, Mock
from django.test import TestCase, RequestFactory from django.test import TestCase, RequestFactory
from django.urls import reverse from django.urls import reverse

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class OptimerConfig(AppConfig): class OptimerConfig(AppConfig):
name = 'allianceauth.optimer' name = 'allianceauth.optimer'
label = 'optimer' label = 'optimer'
verbose_name = _('Fleet Operations')

View File

@@ -14,7 +14,7 @@
<th class="text-center">{% translate "Operation" %}</th> <th class="text-center">{% translate "Operation" %}</th>
<th class="text-center">{% translate "Type" %}</th> <th class="text-center">{% translate "Type" %}</th>
<th class="text-center">{% translate "Form Up System" %}</th> <th class="text-center">{% translate "Form Up System" %}</th>
<th class="text-center">{% translate "EVE Time" %}</th> <th class="text-center">{% translate "EVE time" %}</th>
</tr> </tr>
</thead> </thead>

View File

@@ -25,7 +25,7 @@
<div> <div>
<div class="text-center mb-3"> <div class="text-center mb-3">
<div class="badge bg-primary text-start"> <div class="badge bg-primary text-start">
<b>{% translate "Current Eve Time:" %}</b> <b>{% translate "Current EVE time:" %}</b>
<span id="current-time"></span> <span id="current-time"></span>
</div> </div>
</div> </div>
@@ -127,8 +127,8 @@
}; };
/** /**
* Get the current Eve time as a string * Get the current EVE time as a string
* @returns {string} Eve time string * @returns {string} EVE time string
*/ */
const updateClock = () => { const updateClock = () => {
document.getElementById("current-time").innerHTML = getCurrentEveTimeString(); document.getElementById("current-time").innerHTML = getCurrentEveTimeString();

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class PermissionsToolConfig(AppConfig): class PermissionsToolConfig(AppConfig):
name = 'allianceauth.permissions_tool' name = 'allianceauth.permissions_tool'
label = 'permissions_tool' label = 'permissions_tool'
verbose_name = _('Permissions Audit')

View File

@@ -1,7 +1,7 @@
import logging import logging
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission, User
from django.db.models import Count from django.db.models import Count
from django.shortcuts import render, Http404 from django.shortcuts import render, Http404

View File

@@ -61,6 +61,7 @@ CELERYBEAT_SCHEDULE = {
'esi_cleanup_token': { 'esi_cleanup_token': {
'task': 'esi.tasks.cleanup_token', 'task': 'esi.tasks.cleanup_token',
'schedule': crontab(minute='0', hour='0'), 'schedule': crontab(minute='0', hour='0'),
'apply_offset': True,
}, },
'run_model_update': { 'run_model_update': {
'task': 'allianceauth.eveonline.tasks.run_model_update', 'task': 'allianceauth.eveonline.tasks.run_model_update',
@@ -119,7 +120,12 @@ LANGUAGES = ( # Sorted by Language Code alphabetical order + English at top
# Django's language codes are different from some of the libraries we use, # Django's language codes are different from some of the libraries we use,
# so we need to map them. # so we need to map them.
# When adding a new language, please remember to add it to the mapping
# and add the language files to their respective directories under `allianceauth/static/allianceauth/libs/`.
LANGUAGE_MAPPING = { LANGUAGE_MAPPING = {
# See https://github.com/DataTables/Plugins/tree/master/i18n for available languages
# (We use the JSON files)
# `allianceauth/static/allianceauth/libs/DataTables/Plugins/{version}/i18n/` for the files
"DataTables": { "DataTables": {
"cs-cz": "cs", "cs-cz": "cs",
"de": "de-DE", "de": "de-DE",
@@ -134,6 +140,8 @@ LANGUAGE_MAPPING = {
"uk": "uk", "uk": "uk",
"zh-hans": "zh-HANT", "zh-hans": "zh-HANT",
}, },
# See https://github.com/moment/moment/tree/master/locale for available languages
# `allianceauth/static/allianceauth/libs/moment.js/{version}/locale/` for the files
"MomentJS": { "MomentJS": {
"cs-cz": "cs", "cs-cz": "cs",
"de": "de", "de": "de",

View File

@@ -1,49 +1,48 @@
[program:beat] [program:beat]
command={{ celery }} -A {{ project_name }} beat command = {{ celery }} -A {{ project_name }} beat
directory={{ project_directory }} directory = {{ project_directory }}
user=allianceserver user = allianceserver
stdout_logfile={{ project_directory }}/log/beat.log stdout_logfile = {{ project_directory }}/log/%(program_name)s.log
stderr_logfile={{ project_directory }}/log/beat.log stderr_logfile = {{ project_directory }}/log/%(program_name)s.log
autostart=true autostart = true
autorestart=true autorestart = true
startsecs=10 startsecs = 10
priority=998 priority = 998
[program:worker] [program:worker]
command={{ celery }} -A {{ project_name }} worker --pool=threads --concurrency=5 -n %(program_name)s_%(process_num)02d command = {{ celery }} -A {{ project_name }} worker --pool=threads --concurrency=5 -n %(program_name)s_%(process_num)02d
directory={{ project_directory }} directory = {{ project_directory }}
user=allianceserver user = allianceserver
numprocs=1 numprocs = 1
process_name=%(program_name)s_%(process_num)02d process_name = %(program_name)s_%(process_num)02d
stdout_logfile={{ project_directory }}/log/worker.log stdout_logfile = {{ project_directory }}/log/%(program_name)s.log
stderr_logfile={{ project_directory }}/log/worker.log stderr_logfile = {{ project_directory }}/log/%(program_name)s.log
autostart=true autostart = true
autorestart=true autorestart = true
startsecs=10 startsecs = 10
stopwaitsecs = 600 stopwaitsecs = 600
killasgroup=true killasgroup = true
priority=998 priority = 998
{% if gunicorn %} {% if gunicorn %}
[program:gunicorn] [program:gunicorn]
user = allianceserver user = allianceserver
directory={{ project_directory }} directory = {{ project_directory }}
command={{ gunicorn }} {{ project_name }}.wsgi --workers=3 --timeout 120 command = {{ gunicorn }} {{ project_name }}.wsgi --workers=3 --timeout 120
stdout_logfile={{ project_directory }}/log/gunicorn.log stdout_logfile = {{ project_directory }}/log/%(program_name)s.log
stderr_logfile={{ project_directory }}/log/gunicorn.log stderr_logfile = {{ project_directory }}/log/%(program_name)s.log
autostart=true autostart = true
autorestart=true autorestart = true
stopsignal=INT stopsignal = INT
{% endif %} {% endif %}
[eventlistener:memmon] [eventlistener:memmon]
command={{ memmon }} -p worker_00=256MB -p gunicorn=256MB command = {{ memmon }} -p worker_00=256MB -p gunicorn=256MB
directory={{ project_directory }} directory = {{ project_directory }}
events=TICK_60 events = TICK_60
stdout_logfile={{ project_directory }}/log/memmon.log stdout_logfile = {{ project_directory }}/log/memmon.log
stderr_logfile={{ project_directory }}/log/memmon.log stderr_logfile = {{ project_directory }}/log/memmon.log
[group:{{ project_name }}] [group:{{ project_name }}]
programs=beat,worker{% if gunicorn %},gunicorn{% endif %} programs = beat,worker{% if gunicorn %},gunicorn{% endif %}
priority=999 priority = 999

View File

@@ -1,9 +1,11 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ServicesConfig(AppConfig): class ServicesConfig(AppConfig):
name = 'allianceauth.services' name = 'allianceauth.services'
label = 'services' label = 'services'
verbose_name = _('Services')
def ready(self): def ready(self):
from . import signals from . import signals

View File

@@ -1,6 +1,13 @@
from string import Formatter from string import Formatter
from django.urls import include, re_path
from typing import Iterable, Optional from typing import Iterable, Optional
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.template.loader import render_to_string
from django.urls import include, re_path
from django.utils.functional import cached_property
from allianceauth.hooks import get_hooks from allianceauth.hooks import get_hooks
from allianceauth.menu.hooks import MenuItemHook from allianceauth.menu.hooks import MenuItemHook
from django.conf import settings from django.conf import settings

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class DiscordServiceConfig(AppConfig): class DiscordServiceConfig(AppConfig):
name = 'allianceauth.services.modules.discord' name = 'allianceauth.services.modules.discord'
label = 'discord' label = 'discord'
verbose_name = _('Discord Service')

View File

@@ -1,5 +1,5 @@
import logging import logging
from typing import Optional from typing import ClassVar, Optional
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
@@ -59,7 +59,7 @@ class DiscordUser(models.Model):
help_text='Date & time this service account was activated' help_text='Date & time this service account was activated'
) )
objects = DiscordUserManager() objects: ClassVar[DiscordUserManager] = DiscordUserManager()
class Meta: class Meta:
permissions = ( permissions = (

View File

@@ -37,6 +37,7 @@ import random
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from allianceauth.services.modules.discord.models import DiscordUser
from allianceauth.utils.cache import get_redis_client from allianceauth.utils.cache import get_redis_client
logger = logging.getLogger('allianceauth') logger = logging.getLogger('allianceauth')

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class DiscourseServiceConfig(AppConfig): class DiscourseServiceConfig(AppConfig):
name = 'allianceauth.services.modules.discourse' name = 'allianceauth.services.modules.discourse'
label = 'discourse' label = 'discourse'
verbose_name = _('Discourse Service')

View File

@@ -1,4 +1,5 @@
import logging import logging
import requests
import re import re
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache

View File

@@ -1,7 +1,7 @@
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect from django.shortcuts import render, redirect
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .manager import DiscourseManager from .manager import DiscourseManager

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ExampleServiceConfig(AppConfig): class ExampleServiceConfig(AppConfig):
name = 'allianceauth.services.modules.example' name = 'allianceauth.services.modules.example'
label = 'example_service' label = 'example_service'
verbose_name = _('Example Service')

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class Ips4ServiceConfig(AppConfig): class Ips4ServiceConfig(AppConfig):
name = 'allianceauth.services.modules.ips4' name = 'allianceauth.services.modules.ips4'
label = 'ips4' label = 'ips4'
verbose_name = _('IPS4 Service')

View File

@@ -1,3 +1,4 @@
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from allianceauth.services.hooks import NameFormatter from allianceauth.services.hooks import NameFormatter

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class MumbleServiceConfig(AppConfig): class MumbleServiceConfig(AppConfig):
name = 'allianceauth.services.modules.mumble' name = 'allianceauth.services.modules.mumble'
label = 'mumble' label = 'mumble'
verbose_name = _('Mumble Service')

View File

@@ -1,5 +1,6 @@
import random import random
import string import string
from typing import ClassVar
from passlib.hash import bcrypt_sha256 from passlib.hash import bcrypt_sha256
from django.db import models from django.db import models
@@ -116,7 +117,7 @@ class MumbleUser(AbstractServiceModel):
help_text="Timestamp of the users Last Disconnection to Mumble" help_text="Timestamp of the users Last Disconnection to Mumble"
) )
objects = MumbleManager() objects: ClassVar[MumbleManager] = MumbleManager()
def __str__(self): def __str__(self):
return self.username return self.username

View File

@@ -8,7 +8,7 @@
{% endblock page_title %} {% endblock page_title %}
{% block header_nav_brand %} {% block header_nav_brand %}
<a class="navbar-brand">{% trans "Mumble History" %} - {{ mumble_url }}</a> {% trans "Mumble History" %} - {{ mumble_url }}
{% endblock header_nav_brand %} {% endblock header_nav_brand %}
{% block header_nav_collapse_left %} {% block header_nav_collapse_left %}
@@ -87,7 +87,7 @@
<script> <script>
$(document).ready(function () { $(document).ready(function () {
const MUMBLESTATS_DATETIME_FORMAT = 'YYYY-MM-DD, HH:mm'; const MUMBLESTATS_DATETIME_FORMAT = 'LLL';
'use strict'; 'use strict';
@@ -104,15 +104,33 @@
{ data: 'version' }, { data: 'version' },
{ {
data: 'last_connect', data: 'last_connect',
render: (data) => { render: {
return moment(data).utc().format(MUMBLESTATS_DATETIME_FORMAT); _: (data) => {
return data === null
? ''
: moment(data)
.utc()
.format(MUMBLESTATS_DATETIME_FORMAT);
},
sort: (data) => {
return data === null ? '' : data;
}
}, },
className: 'text-end', className: 'text-end',
}, },
{ {
data: 'last_disconnect', data: 'last_disconnect',
render: (data) => { render: {
return moment(data).utc().format(MUMBLESTATS_DATETIME_FORMAT); _: (data) => {
return data === null
? ''
: moment(data)
.utc()
.format(MUMBLESTATS_DATETIME_FORMAT);
},
sort: (data) => {
return data === null ? '' : data;
}
}, },
className: 'text-end', className: 'text-end',
}, },

View File

@@ -48,9 +48,9 @@
</a> </a>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if request.user.is_superuser %} {% if perms.mumble.view_connection_history %}
<a class="btn btn-primary" type="button" id="btnMumbleConnectionHistory" href="{% url 'mumble:connection_history' %}" title="{% translate 'Mumble Connection History' %}"> <a class="btn btn-primary" type="button" id="btnMumbleConnectionHistory" href="{% url 'mumble:connection_history' %}" title="{% translate 'Mumble Connection History' %}">
<i class="fa-solid fa-clock-rotate-left"></i> History <i class="fa-solid fa-clock-rotate-left"></i> History
</a> </a>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class OpenfireServiceConfig(AppConfig): class OpenfireServiceConfig(AppConfig):
name = 'allianceauth.services.modules.openfire' name = 'allianceauth.services.modules.openfire'
label = 'openfire' label = 'openfire'
verbose_name = _('Openfire Service')

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class Phpbb3ServiceConfig(AppConfig): class Phpbb3ServiceConfig(AppConfig):
name = 'allianceauth.services.modules.phpbb3' name = 'allianceauth.services.modules.phpbb3'
label = 'phpbb3' label = 'phpbb3'
verbose_name = _('phpBB3 Service')

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class SmfServiceConfig(AppConfig): class SmfServiceConfig(AppConfig):
name = 'allianceauth.services.modules.smf' name = 'allianceauth.services.modules.smf'
label = 'smf' label = 'smf'
verbose_name = _('SMF Service')

View File

@@ -1,9 +1,11 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class Teamspeak3ServiceConfig(AppConfig): class Teamspeak3ServiceConfig(AppConfig):
name = 'allianceauth.services.modules.teamspeak3' name = 'allianceauth.services.modules.teamspeak3'
label = 'teamspeak3' label = 'teamspeak3'
verbose_name = _('TeamSpeak 3 Service')
def ready(self): def ready(self):
from . import signals from . import signals

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class XenforoServiceConfig(AppConfig): class XenforoServiceConfig(AppConfig):
name = 'allianceauth.services.modules.xenforo' name = 'allianceauth.services.modules.xenforo'
label = 'xenforo' label = 'xenforo'
verbose_name = _('Xenforo Service')

View File

@@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
<div class="card text-center m-2" style="min-width: 18rem; min-height: 18rem;"> <div class="card text-center mx-2 mb-3" style="min-width: 18rem; min-height: 18rem;">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{% block title %}{% endblock title %}</h5> <h5 class="card-title">{% block title %}{% endblock title %}</h5>

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class SRPConfig(AppConfig): class SRPConfig(AppConfig):
name = 'allianceauth.srp' name = 'allianceauth.srp'
label = 'srp' label = 'srp'
verbose_name = _('Ship Replacement')

View File

@@ -172,7 +172,7 @@ ul.list-group.list-group-horizontal > li.list-group-item {
} }
} }
/* eve time in navbar /* EVE time in navbar
------------------------------------------------------------------------------------- */ ------------------------------------------------------------------------------------- */
@media all { @media all {
.nav-item-eve-time .eve-time-wrapper { .nav-item-eve-time .eve-time-wrapper {

View File

@@ -14,7 +14,7 @@ $(document).ready(() => {
}; };
/** /**
* Start the Eve time clock in the top menu bar * Start the EVE time clock in the top menu bar
*/ */
setInterval(() => { setInterval(() => {
renderClock($('.eve-time-wrapper .eve-time-clock')); renderClock($('.eve-time-wrapper .eve-time-clock'));

View File

@@ -28,7 +28,7 @@ const getDurationString = (duration) => { // eslint-disable-line no-unused-vars
}; };
/** /**
* returns the current eve time as a formatted string * returns the current EVE time as a formatted string
* *
* condition: * condition:
* only if moment.js is loaded before, * only if moment.js is loaded before,

View File

@@ -12,11 +12,7 @@
<ul class="list-group"> <ul class="list-group">
{% for notif in notifications %} {% for notif in notifications %}
<li class="list-group-item"> <li class="list-group-item">
{% if notif.state == 'opened' %} <span class="badge bg-success me-2">{% translate "Open" %}</span>
<span class="badge bg-success me-2">{% translate "Open" %}</span>
{% else %}
<span class="badge bg-danger me-2">{% translate "Closed" %}</span>
{% endif %}
<a href="{{ notif.web_url }}" target="_blank">#{{ notif.iid }} {{ notif.title }}</a> <a href="{{ notif.web_url }}" target="_blank">#{{ notif.iid }} {{ notif.title }}</a>
</li> </li>
{% empty %} {% empty %}

View File

@@ -12,11 +12,10 @@
<!-- End Required meta tags --> <!-- End Required meta tags -->
<!-- Meta tags --> <!-- Meta tags -->
<!-- TODO Bundle all the site specific stuff up into its own template for easy override --> {% include 'allianceauth/opengraph.html' %}
<meta name="description" content="">
<meta name="author" content="">
{% include 'allianceauth/icons.html' %} {% include 'allianceauth/icons.html' %}
<!-- Meta tags --> <!-- Meta tags -->
<title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title> <title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title>
{% theme_css %} {% theme_css %}
@@ -25,12 +24,11 @@
{% include 'bundles/auth-framework-css.html' %} {% include 'bundles/auth-framework-css.html' %}
<style> <style>
.navbar-toggler.collapsed{ @media all {
transform: rotate(180deg); .nav-padding {
} margin-top: {% header_padding_size %} !important;
max-height: calc(100vh - {% header_padding_size %}) !important;
.nav-padding { }
padding-top: {% header_padding_size %} !important;
} }
</style> </style>
@@ -63,6 +61,11 @@
</ul> </ul>
<ul id="nav-right-character-control" class="nav navbar-nav"> <ul id="nav-right-character-control" class="nav navbar-nav">
<li class="nav-item ms-lg-2 py-2 py-lg-1 col-12 col-lg-auto d-none">
<div class="vr d-none d-lg-flex h-100 mx-2 text-white-50"></div>
<hr class="d-lg-none my-2 text-body text-white-50">
</li>
{% block header_nav_user_character_control %} <!-- Default to add char and swap main --> {% block header_nav_user_character_control %} <!-- Default to add char and swap main -->
{% include 'allianceauth/top-menu-rh-default.html' %} {% include 'allianceauth/top-menu-rh-default.html' %}
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,8 @@
{% load static %}
<meta property="og:title" content="{{ SITE_NAME }}">
<meta property="og:image" content="{{ SITE_URL }}{% static 'allianceauth/icons/apple-touch-icon.png' %}">
<meta property="og:description" content="Alliance Auth - An auth system for EVE Online to help in-game organizations manage online service access.">
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{ request.META.HTTP_HOST }}">
<meta property="og:url" content="{{ request.path }}">

View File

@@ -20,7 +20,7 @@
<div class="collapse navbar-collapse auth-menus-collapse"> <div class="collapse navbar-collapse auth-menus-collapse">
<ul class="nav navbar-nav navbar-right navbar-pills"> <ul class="nav navbar-nav navbar-right navbar-pills">
<li class="nav-item-eve-time"> <li class="nav-item-eve-time">
<div class="eve-time-wrapper">{% translate "Eve Time" %}: <span class="eve-time-clock"></span></div> <div class="eve-time-wrapper">{% translate "EVE time" %}: <span class="eve-time-clock"></span></div>
</li> </li>
{% if user.is_authenticated %} {% if user.is_authenticated %}

View File

@@ -1,3 +1,3 @@
{% load static %} {% load static %}
<img src="{% static 'allianceauth/images/auth-logo.svg' %}" width="{{ logo_width|default:"128px" }}" height="{% if logo_height %}{{ logo_ }}{% else %}{{ logo_width|default:"128px" }}{% endif %}" alt="{{ SITE_NAME }}"> <img src="{% static 'allianceauth/images/auth-logo.svg' %}" width="{{ logo_width|default:"128px" }}" height="{% if logo_height %}{{ logo_height }}{% else %}{{ logo_width|default:"128px" }}{% endif %}" alt="{{ SITE_NAME }}">

View File

@@ -67,6 +67,26 @@ def get_datatables_language_static(language: str) -> str:
return static_url return static_url
@register.simple_tag
def get_relative_datatables_language_path(language: str) -> str:
"""
Get the correct language code URL for DataTables (relative path to the static folder)
:param language: Django's language code
:type language: str
:return: Mapped language code
:rtype: str
"""
mapped_language = get_datatable_language_code(language)
static_url = (
f"allianceauth/libs/DataTables/Plugins/2.2.1/i18n/{mapped_language}.json"
if mapped_language
else ""
)
return static_url
@register.simple_tag @register.simple_tag
def get_momentjs_language_static(language: str) -> str: def get_momentjs_language_static(language: str) -> str:
@@ -88,3 +108,24 @@ def get_momentjs_language_static(language: str) -> str:
) )
return static_url return static_url
@register.simple_tag
def get_relative_momentjs_language_path(language: str) -> str:
"""
Get the correct language code URL for Moment.JS (relative path to the static folder)
:param language: Django's language code
:type language: str
:return: Mapped language code path
:rtype: str
"""
mapped_language = get_momentjs_language_code(language)
static_url = (
f"allianceauth/libs/moment.js/2.29.4/locale/{mapped_language}.js"
if mapped_language
else ""
)
return static_url

View File

@@ -1,6 +1,12 @@
from unittest import mock
from django.contrib.auth.models import User, Group, Permission from django.contrib.auth.models import User, Group, Permission
from django.test import TestCase from django.test import TestCase
from allianceauth.eveonline.models import (
EveCorporationInfo, EveAllianceInfo, EveCharacter
)
from .auth_utils import AuthUtils from .auth_utils import AuthUtils

Some files were not shown because too many files have changed in this diff Show More