Merge branch 'aa-framework-part-2' into 'v4.x'

Alliance Auth Framework (Part 2)

See merge request allianceauth/allianceauth!1558
This commit is contained in:
Ariel Rin 2023-12-25 09:46:57 +00:00
commit b28b51916c
52 changed files with 614 additions and 179 deletions

View File

@ -14,15 +14,13 @@ Needs to be called with a context containing three objects:
{% block page_title %}Evelinks Examples{% endblock page_title %}
{% block content %}
<div>
{% include "framework/header/page-header.html" with title="Evelinks templatetags examples" %}
<div class="col-lg-12">
<h1 class="page-header text-center mb-3">Evelinks templatetags examples</h1>
<div class="col-lg-12 container">
<h2>profile URLs</h2>
<div class="rows">
<div class="col-md-4">
<h3>evewho</h3>
<p><a href="{{ my_character|evewho_character_url }}">character from character object</a></p>
@ -57,7 +55,6 @@ Needs to be called with a context containing three objects:
<h2>image URLs</h2>
<div class="rows">
<div class="col-md-4">
<p>character from ID: <img src="{{ my_character.character_id|character_portrait_url:128 }}"></p>
<p>character from character object: <img src="{{ my_character|character_portrait_url:128 }}"></p>
@ -77,5 +74,4 @@ Needs to be called with a context containing three objects:
</div>
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,26 @@
"""
Migration to AA Framework API method
"""
from django.conf import settings
from django.db import migrations, models
import allianceauth.framework.api.user
class Migration(migrations.Migration):
dependencies = [
("fleetactivitytracking", "0006_auto_20180803_0430"),
]
operations = [
migrations.AlterField(
model_name="fatlink",
name="creator",
field=models.ForeignKey(
on_delete=models.SET(allianceauth.framework.api.user.get_sentinel_user),
to=settings.AUTH_USER_MODEL
),
),
]

View File

@ -3,10 +3,7 @@ from django.db import models
from django.utils import timezone
from allianceauth.eveonline.models import EveCharacter
def get_sentinel_user():
return User.objects.get_or_create(username='deleted')[0]
from allianceauth.framework.api.user import get_sentinel_user
class Fatlink(models.Model):

View File

@ -12,9 +12,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Character not found!" %}
</h1>
{% translate "Character not found!" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="col-lg-12 container">
<div class="row">

View File

@ -13,9 +13,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Create Fatlink" %}
</h1>
{% translate "Create Fatlink" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div>
{% if badrequest %}

View File

@ -14,16 +14,16 @@
<div>
<h1 class="page-header text-center mb-3">
{% translate "Edit fatlink" %} "{{ fatlink }}"
<div class="text-end">
<form>
<button type="submit" onclick="return confirm('{% translate "Are you sure?" %}')" class="btn btn-danger" name="deletefat" value="True">
{% translate "Delete fat" %}
</button>
</form>
</div>
</h1>
<div class="text-end mb-3">
<form>
<button type="submit" onclick="return confirm('{% translate "Are you sure?" %}')" class="btn btn-danger" name="deletefat" value="True">
{% translate "Delete fat" %}
</button>
</form>
</div>
<div class="card card-default">
<div class="card-header">
<div class="card-title mb-0">{% translate "Registered characters" %}</div>

View File

@ -14,20 +14,20 @@
<div>
<h1 class="page-header text-center mb-3">
{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
{% if char_id %}
<div class="text-end">
<a href="{% url 'fatlink:user_statistics_month' char_id previous_month|date:'Y' previous_month|date:'m' %}" class="btn btn-info">
{% translate "Previous month" %}
</a>
<a href="{% url 'fatlink:user_statistics_month' char_id next_month|date:'Y' next_month|date:'m' %}" class="btn btn-info">
{% translate "Next month" %}
</a>
</div>
{% endif %}
</h1>
<div class="card card-default mb-4">
{% if char_id %}
<div class="text-end mb-3">
<a href="{% url 'fatlink:user_statistics_month' char_id previous_month|date:'Y' previous_month|date:'m' %}" class="btn btn-info">
{% translate "Previous month" %}
</a>
<a href="{% url 'fatlink:user_statistics_month' char_id next_month|date:'Y' next_month|date:'m' %}" class="btn btn-info">
{% translate "Next month" %}
</a>
</div>
{% endif %}
<div class="card card-default mb-3">
<div class="card-header">
<div class="card-title mb-0">
{% blocktranslate count links=n_fats trimmed %}

View File

@ -14,16 +14,16 @@
<div>
<h1 class="page-header text-center mb-3">
{% blocktranslate %}Participation data statistics for {{ year }}{% endblocktranslate %}
<div class="text-end">
<a href="{% url "fatlink:personal_statistics_year" previous_year %}" class="btn btn-info"><i class="fa-solid fa-chevron-left"></i> {% translate "Previous year" %}</a>
{% if next_year %}
<a href="{% url "fatlink:personal_statistics_year" next_year %}" class="btn btn-info">{% translate "Next year" %} <i class="fa-solid fa-chevron-right"></i></a>
{% endif %}
</div>
</h1>
<div class="text-end mb-3">
<a href="{% url "fatlink:personal_statistics_year" previous_year %}" class="btn btn-info"><i class="fa-solid fa-chevron-left"></i> {% translate "Previous year" %}</a>
{% if next_year %}
<a href="{% url "fatlink:personal_statistics_year" next_year %}" class="btn btn-info">{% translate "Next year" %} <i class="fa-solid fa-chevron-right"></i></a>
{% endif %}
</div>
<div class="col-lg-2 offset-lg-5">
<table class="table table-responsive">
<tr>

View File

@ -11,19 +11,19 @@
{% endblock header_nav_brand %}
{% block content %}
<div class="col-lg-12">
<div>
<h1 class="page-header text-center mb-3">
{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
<div class="text-end">
<a href="{% url "fatlink:statistics_corp_month" corpid previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
{% if next_month %}
<a href="{% url "fatlink:statistics_corp_month" corpid next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
{% endif %}
</div>
</h1>
<div class="text-end mb-3">
<a href="{% url "fatlink:statistics_corp_month" corpid previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
{% if next_month %}
<a href="{% url "fatlink:statistics_corp_month" corpid next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
{% endif %}
</div>
{% if fatStats %}
<div class="table-responsive">
<table class="table table-striped">

View File

@ -14,16 +14,16 @@
<div>
<h1 class="page-header text-center mb-3">
{% blocktranslate %}Participation data statistics for {{ month }}, {{ year }}{% endblocktranslate %}
<div class="text-end">
<a href="{% url "fatlink:statistics_month" previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
{% if next_month %}
<a href="{% url 'fatlink:statistics_month' next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
{% endif %}
</div>
</h1>
<div class="text-end mb-3">
<a href="{% url "fatlink:statistics_month" previous_month|date:"Y" previous_month|date:"m" %}" class="btn btn-info">{% translate "Previous month" %}</a>
{% if next_month %}
<a href="{% url 'fatlink:statistics_month' next_month|date:"Y" next_month|date:"m" %}" class="btn btn-info">{% translate "Next month" %}</a>
{% endif %}
</div>
{% if fatStats %}
<div class="table-responsive">
<table class="table table-striped">

View File

@ -11,10 +11,9 @@
{% endblock header_nav_brand %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center mb-3">
{% translate "Participation data" %}
</h1>
<div>
{% translate "Participation data" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="table-responsive">
<table class="table table-striped">

View File

@ -0,0 +1,3 @@
"""
Alliance Auth Framework
"""

View File

@ -0,0 +1,64 @@
"""
Alliance Auth User API
"""
from typing import Optional
from django.contrib.auth.models import User
from allianceauth.eveonline.models import EveCharacter
def get_sentinel_user() -> User:
"""
Get the sentinel user or create one
:return:
"""
return User.objects.get_or_create(username="deleted")[0]
def get_main_character_from_user(user: User) -> Optional[EveCharacter]:
"""
Get the main character from a user
:param user:
:type user:
:return:
:rtype:
"""
if user is None:
return None
try:
main_character = user.profile.main_character
except AttributeError:
return None
return main_character
def get_main_character_name_from_user(user: User) -> str:
"""
Get the main character name from a user
:param user:
:type user:
:return:
:rtype:
"""
if user is None:
sentinel_user = get_sentinel_user()
return sentinel_user.username
main_character = get_main_character_from_user(user=user)
try:
username = main_character.character_name
except AttributeError:
return str(user)
return username

View File

@ -0,0 +1,14 @@
"""
Framework App Config
"""
from django.apps import AppConfig
class FrameworkConfig(AppConfig):
"""
Framework App Config
"""
name = "allianceauth.framework"
label = "framework"

View File

@ -0,0 +1,13 @@
{#Usage:#}
{# {% include "framework/header/page-header.html" with title="Foobar" subtitle="Barfoo" %}#}
{% if title %}
<h1 class="page-header text-center mb-3">
{{ title }}
{% if subtitle %}
<br>
<small>{{ subtitle }}</small>
{% endif %}
</h1>
{% endif %}

View File

@ -0,0 +1,3 @@
"""
Initializing our tests
"""

View File

@ -0,0 +1,179 @@
"""
Test sentinel user
"""
import re
# Django
from django.contrib.auth.models import User
from django.test import TestCase
# Alliance Auth
from allianceauth.framework.api.user import (
get_sentinel_user,
get_main_character_from_user,
get_main_character_name_from_user
)
from allianceauth.tests.auth_utils import AuthUtils
class TestSentinelUser(TestCase):
"""
Tests for the sentinel user
"""
def test_should_create_user_when_it_does_not_exist(self) -> None:
"""
Test should create a sentinel user when it doesn't exist
:return:
:rtype:
"""
# when
user = get_sentinel_user()
# then
self.assertEqual(first=user.username, second="deleted")
def test_should_return_user_when_it_does(self) -> None:
"""
Test should return sentinel user when it exists
:return:
:rtype:
"""
# given
User.objects.create_user(username="deleted")
# when
user = get_sentinel_user()
# then
self.assertEqual(first=user.username, second="deleted")
class TestGetMainForUser(TestCase):
"""
Tests for get_main_character_from_user
"""
@classmethod
def setUpClass(cls) -> None:
"""
Set up groups and users
"""
super().setUpClass()
cls.character_name = "William T. Riker"
cls.character_name_2 = "Christopher Pike"
cls.username = re.sub(pattern=r"[^\w\d@\.\+-]", repl="_", string=cls.character_name)
cls.username_2 = re.sub(
pattern=r"[^\w\d@\.\+-]", repl="_", string=cls.character_name_2
)
cls.user = AuthUtils.create_user(username=cls.username)
cls.user_without_main = AuthUtils.create_user(
username=cls.username_2, disconnect_signals=True
)
cls.character = AuthUtils.add_main_character_2(
user=cls.user, name=cls.character_name, character_id=1001
)
def test_get_main_character_from_user_should_return_character_name(self):
"""
Test should return the main character name for a regular user
:return:
:rtype:
"""
character = get_main_character_from_user(user=self.user)
self.assertEqual(first=character, second=self.character)
def test_get_main_character_from_user_should_return_none_for_no_main_character(self):
"""
Test should return None for User without a main character
:return:
:rtype:
"""
character = get_main_character_from_user(user=self.user_without_main)
self.assertIsNone(obj=character)
def test_get_main_character_from_user_should_none(self):
"""
Test should return None when user is None
:return:
:rtype:
"""
user = None
character = get_main_character_from_user(user=user)
self.assertIsNone(obj=character)
def test_get_main_character_name_from_user_should_return_character_name(self):
"""
Test should return the main character name for a regular user
:return:
:rtype:
"""
character_name = get_main_character_name_from_user(user=self.user)
self.assertEqual(first=character_name, second=self.character_name)
def test_get_main_character_name_from_user_should_return_user_name(self):
"""
Test should return just the username for a user without a main character
:return:
:rtype:
"""
character_name = get_main_character_name_from_user(user=self.user_without_main)
self.assertEqual(first=character_name, second=self.username_2)
def test_get_main_character_name_from_user_should_return_sentinel_user(self):
"""
Test should return "deleted" as username (Sentinel User)
:return:
:rtype:
"""
user = get_sentinel_user()
character_name = get_main_character_name_from_user(user=user)
self.assertEqual(first=character_name, second="deleted")
def test_get_main_character_name_from_user_should_return_sentinel_user_for_none(self):
"""
Test should return "deleted" (Sentinel User) if user is None
:return:
:rtype:
"""
user = None
character_name = get_main_character_name_from_user(user=user)
self.assertEqual(first=character_name, second="deleted")

View File

@ -12,9 +12,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Choose a Corp" %}
</h1>
{% translate "Choose a Corp" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
{% if choices %}
<div class="card card-primary">

View File

@ -13,17 +13,18 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">{% translate "Personal Applications" %}
<div class="text-end">
{% if create %}
<a href="{% url 'hrapplications:create_view' %}">
<button type="button" class="btn btn-success">{% translate "Create Application" %}</button>
</a>
{% else %}
<button type="button" class="btn btn-success" disabled>{% translate "Create Application" %}</button>
{% endif %}
</div>
</h1>
{% translate "Personal Applications" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="text-end mb-3">
{% if create %}
<a href="{% url 'hrapplications:create_view' %}">
<button type="button" class="btn btn-success">{% translate "Create Application" %}</button>
</a>
{% else %}
<button type="button" class="btn btn-success" disabled>{% translate "Create Application" %}</button>
{% endif %}
</div>
{% if personal_apps %}
<div class="card card-default mb-3">
@ -68,16 +69,17 @@
{% endif %}
{% if perms.auth.human_resources %}
<h1 class="page-header text-center mb-3">{% translate "Application Management" %}
<div class="text-end">
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#modal-hr-search">
{% translate "Search Applications" %}
</button>
</div>
</h1>
{% translate "Application Management" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="card card-default mt-4">
<div class="text-end mb-3">
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#modal-hr-search">
{% translate "Search Applications" %}
</button>
</div>
<div class="card card-default">
<div class="card-body clearfix">
<ul class="nav nav-tabs" id="application-list" role="tablist">
<li class="nav-item" role="presentation">

View File

@ -14,9 +14,8 @@
{% block content %}
<div>
{% if perms.auth.human_resources %}
<h1 class="page-header text-center mb-3">
{% translate "Application Search Results" %}
</h1>
{% translate "Application Search Results" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="text-end mb-3">
<!-- Button trigger modal -->

View File

@ -13,7 +13,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">{% translate "View Application" %}</h1>
{% translate "View Application" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div>
{% if app.approved %}

View File

@ -18,9 +18,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Create Fleet Operation" %}
</h1>
{% translate "Create Fleet Operation" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="card card-primary border-0">
<div class="card-header">

View File

@ -18,9 +18,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Update Fleet Operation" %}
</h1>
{% translate "Update Fleet Operation" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="card card-primary border-0">
<div class="card-header">

View File

@ -25,6 +25,7 @@ INSTALLED_APPS = [
'django_bootstrap5', # https://github.com/zostera/django-bootstrap5
'sortedm2m',
'esi',
'allianceauth.framework',
'allianceauth.authentication',
'allianceauth.services',
'allianceauth.eveonline',

View File

@ -7,9 +7,8 @@
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center mb-3">
{% translate "Verify TeamSpeak3 Identity" %}
</h1>
{% translate "Verify TeamSpeak3 Identity" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="row justify-content-center">
<div class="col-md-4">

View File

@ -11,7 +11,7 @@
{% endblock header_nav_brand %}
{% block content %}
<div class="col-lg-12">
<div>
<h1 class="page-header text-center mb-3">
{% blocktranslate with service_name=view.service_name|title %}{{ service_name }} Credentials{% endblocktranslate %}
</h1>

View File

@ -17,9 +17,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Create SRP Fleet" %}
</h1>
{% translate "Create SRP Fleet" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="card card-primary border-0">
<div class="card-header">

View File

@ -13,23 +13,22 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "SRP Fleet Data" %}
{% translate "SRP Fleet Data" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="text-end">
{% if perms.auth.srp_management %}
{% if fleet_status == "Completed" %}
<a href="{% url 'srp:mark_uncompleted' fleet_id %}" class="btn btn-warning">
{% translate "Mark Incomplete" %}
</a>
{% else %}
<a href="{% url 'srp:mark_completed' fleet_id %}" class="btn btn-success">
{% translate "Mark Completed" %}
</a>
{% endif %}
<div class="text-end mb-3">
{% if perms.auth.srp_management %}
{% if fleet_status == "Completed" %}
<a href="{% url 'srp:mark_uncompleted' fleet_id %}" class="btn btn-warning">
{% translate "Mark Incomplete" %}
</a>
{% else %}
<a href="{% url 'srp:mark_completed' fleet_id %}" class="btn btn-success">
{% translate "Mark Completed" %}
</a>
{% endif %}
</div>
</h1>
{% endif %}
</div>
{% if srpfleetrequests %}
<form method="POST">

View File

@ -13,9 +13,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Create SRP Request" %}
</h1>
{% translate "Create SRP Request" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="card card-primary border-0">
<div class="card-header">

View File

@ -13,9 +13,8 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Update AAR Link" %}
</h1>
{% translate "Update AAR Link" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<div class="card card-primary border-0">
<div class="card-header">

View File

@ -5,8 +5,8 @@
{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center mb-3">{{ error_title }}</h1>
<div>
{% include "framework/header/page-header.html" with title=error_title %}
<div class="text-center">
<svg

View File

@ -1,3 +1,3 @@
{% load static %}
<link href="{% static 'allianceauth/css/auth-framework.css' %}" rel="stylesheet">
<link href="{% static 'allianceauth/framework/css/auth-framework.css' %}" rel="stylesheet">

View File

@ -15,9 +15,10 @@
<h1 class="page-header text-center mb-3">
{% block page_header %}
{% endblock page_header %}
{% include "timerboard/index_button.html" %}
</h1>
{% include "timerboard/index_button.html" %}
<div class="card card-primary border-0">
<div class="card-header">
<div class="card-title mb-0">

View File

@ -1,5 +1,5 @@
{% load i18n %}
<div class="text-end">
<div class="text-end mb-3">
<a href="{% url 'timerboard:view' %}" class="btn btn-secondary">{% translate "Back" %}</a>
</div>

View File

@ -12,13 +12,13 @@
{% block content %}
<div>
<h1 class="page-header text-center mb-3">
{% translate "Delete Timer" %}
{% include "timerboard/index_button.html" %}
</h1>
{% translate "Delete Timer" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
{% include "timerboard/index_button.html" %}
<div class="row justify-content-center">
<div class="col-md-2">
<div class="col-md-4">
<div class="row">
<form action="" method="post">
{% csrf_token %}

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,12 @@
# AA Framework
To establish a unified style language throughout Alliance Auth and Community Apps,
Alliance Auth is providing its own CSS framework with a couple of CSS classes.
:::{toctree}
:maxdepth: 1
framework/api
framework/css
framework/templates
:::

View File

@ -1,42 +0,0 @@
# CSS Framework
To establish a unified style language throughout Alliance Auth and Community Apps,
Alliance Auth is providing its own CSS framework with a couple of CSS classes.
## Callout-Boxes
These are similar to the Bootstrap alert/notification boxes, but not as "loud".
Callout-boxes need a base-class (`.aa-callout`) and a modifier-class (e.g.:
`.aa-callout-info` for an info-box). Modifier classes are available for the usual
Bootstrap alert levels "Success", "Info", "Warning" and "Danger".
![Alliance Auth Framework: Callout Boxes](/_static/images/development/css-framework/callout-boxes.png "Alliance Auth Framework: Callout Boxes")
### HTML
```html
<div class="aa-callout aa-callout-success">
<p>
This is a success callout-box.
</p>
</div>
<div class="aa-callout aa-callout-info">
<p>
This is an info callout-box.
</p>
</div>
<div class="aa-callout aa-callout-warning">
<p>
This is a warning callout-box.
</p>
</div>
<div class="aa-callout aa-callout-danger">
<p>
This is a danger callout-box.
</p>
</div>
```

View File

@ -0,0 +1,58 @@
# Alliance Auth Helper-Functions API
## User API
### get_main_character_from_user
This is to get the main character object (`EveCharacter`) of a user.
Given we have a `User` object called `my_user` and we want to get the main character:
```python
# Alliance Auth
from allianceauth.framework.api.user import get_main_character_from_user
main_character = get_main_character_from_user(user=my_user)
```
Now, `main_character` is an `EveCharacter` object, or `None` if the user has no main
character or the user is `None`.
### get_main_character_name_from_user
This is to get the name of the main character of a user.
Given we have a `User` object called `my_user` and we want to get the main character name:
```python
# Alliance Auth
from allianceauth.framework.api.user import get_main_character_name_from_user
main_character = get_main_character_name_from_user(user=my_user)
```
Now, `main_character` is a `string` containing the user's main character name.
If the user has no main character, the username will be returned. If the user is `None`,
the sentinel username (see [get_sentinel_user](#get-sentinel-user)) will be returned.
### get_sentinel_user
This function is useful in models when using `User` model-objects as foreign keys.
Django needs to know what should happen to those relations when the user is being
deleted. To keep the data, you can have Django map this to the sentinel user.
Import:
```python
# Alliance Auth
from allianceauth.framework.api.user import get_sentinel_user
```
And later in your model:
```python
creator = models.ForeignKey(
to=User,
on_delete=models.SET(get_sentinel_user),
)
```

View File

@ -0,0 +1,73 @@
# CSS Framework
To establish a unified style language throughout Alliance Auth and Community Apps,
Alliance Auth is providing its own CSS framework with a couple of CSS classes.
## Cursors
Our CSS framework provides different classes to manipulate the cursor, which are
missing in Bootstrap.
```{eval-rst}
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| CSS Class | Effect | Example |
+======================+========================================================+================================================================================+
| `cursor-default` | System default curser | .. image:: /_static/images/development/aa-framework/css/cursor-default.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-pointer` | Pointer, like it looks like for links and form buttons | .. image:: /_static/images/development/aa-framework/css/cursor-pointer.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-wait` | Wait animation | .. image:: /_static/images/development/aa-framework/css/cursor-wait.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-text` | Text selection cursor | .. image:: /_static/images/development/aa-framework/css/cursor-text.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-move` | 4-arrow-shaped cursor | .. image:: /_static/images/development/aa-framework/css/cursor-move.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-help` | Cursor with a little question mark | .. image:: /_static/images/development/aa-framework/css/cursor-help.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-not-allowed` | Not Allowed sign | .. image:: /_static/images/development/aa-framework/css/cursor-not-allowed.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-inherit` | Inherited from its parent element | |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-zoom-in` | Zoom in symbol | .. image:: /_static/images/development/aa-framework/css/cursor-zoom-in.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
| `cursor-zoom-out` | Zoom out symbol | .. image:: /_static/images/development/aa-framework/css/cursor-zoom-out.png |
+----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------+
```
## Callout-Boxes
These are similar to the Bootstrap alert/notification boxes, but not as "loud".
Callout-boxes need a base-class (`.aa-callout`) and a modifier-class (e.g.:
`.aa-callout-info` for an info-box). Modifier classes are available for the usual
Bootstrap alert levels "Success", "Info", "Warning" and "Danger".
![Alliance Auth Framework: Callout Boxes](/_static/images/development/aa-framework/css/callout-boxes.png "Alliance Auth Framework: Callout Boxes")
### HTML
```html
<div class="aa-callout aa-callout-success">
<p>
This is a success callout-box.
</p>
</div>
<div class="aa-callout aa-callout-info">
<p>
This is an info callout-box.
</p>
</div>
<div class="aa-callout aa-callout-warning">
<p>
This is a warning callout-box.
</p>
</div>
<div class="aa-callout aa-callout-danger">
<p>
This is a danger callout-box.
</p>
</div>
```

View File

@ -0,0 +1,46 @@
# Templates
## Bundles
As bundles, we see templates that load essential CSS and JavaScript and are used
throughout Alliance Auth. These bundles can also be used in your own apps, so you don't
have to load specific CSS or JavaScript yourself.
These bundles include DataTables CSS and JS, jQuery Datepicker CSS and JS, jQueryUI CSS and JS, and more.
A full list of bundles we provide can be found here: https://gitlab.com/allianceauth/allianceauth/-/tree/master/allianceauth/templates/bundles
To use a bundle, you can use the following code in your template (Example for jQueryUI):
```django
{% block extra_css %}
{% include "bundles/jquery-ui-css.html" %}
{% endblock %}
{% block extra_javascript %}
{% include "bundles/jquery-ui-js.html" %}
{% endblock %}
```
## Template Partials
To ensure a unified style language throughout Alliance Auth and Community Apps,
we also provide a couple of template partials. This collection is bound to grow over
time, so best have an eye on this page.
### Page Header
On some pages you want to have a page header. To make this easier, we provide a template partial for this.
To use it, you can use the following code in your template:
```django
{% block content %}
<div>
{% translate "My Page Header" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %}
<p>My page content</p>
</div>
{% endblock %}
```

View File

@ -9,5 +9,5 @@ integrating-services
menu-hooks
url-hooks
logging
css-framework
aa-framework
:::