Compare commits

..

29 Commits

Author SHA1 Message Date
Ariel Rin
2f320bc256 Version Bump 2.9.0a1 2021-05-09 06:45:17 +00:00
Ariel Rin
45b8d42b8e Merge branch 'javascript-modernization' into 'v2.9.x'
Cleanup and modernize JS

See merge request allianceauth/allianceauth!1305
2021-05-09 06:43:58 +00:00
Peter Pfeufer
bd2d19f867 Cleanup and modernize JS 2021-05-09 06:43:58 +00:00
Ariel Rin
0be404baca Merge branch 'py36remove' into 'v2.9.x'
Remove Python36 Testing and Support

See merge request allianceauth/allianceauth!1301
2021-05-09 06:42:56 +00:00
Ariel Rin
e6cee9ac83 Remove Python36 Testing and Support 2021-05-09 06:42:56 +00:00
Ariel Rin
d173a59441 Merge branch 'add-robots-txt' into 'v2.9.x'
Adding robots.txt to fix random 404 message

See merge request allianceauth/allianceauth!1302
2021-04-27 14:11:14 +00:00
Peter Pfeufer
8f59f2549a robots.txt added
This fixes the randomly appearing 404 error in Auth  (issue #1232)
2021-04-15 20:59:25 +02:00
Ariel Rin
630400fee4 Merge branch 'django-3-2-update' into 'v2.9.x'
Django 3.2 compatibility update

See merge request allianceauth/allianceauth!1300
2021-04-08 05:07:01 +00:00
Ariel Rin
1ec6929e91 Merge branch 'python39' into 'v2.9.x'
Add  Python3.9 Testing

See merge request allianceauth/allianceauth!1284
2021-04-08 04:49:15 +00:00
Ariel Rin
8c6bdd8ae2 Add Python3.9 Testing 2021-04-08 04:49:15 +00:00
Peter Pfeufer
e04138bced migrations 2021-04-07 18:40:39 +02:00
Peter Pfeufer
db5ad85811 add new default for PKs 2021-04-07 18:16:33 +02:00
Peter Pfeufer
5b44fd376d revert version cap on Django, so we get 3.2 again 2021-04-07 18:11:01 +02:00
Ariel Rin
0036e8b280 Version Bump v2.8.3 2021-04-07 15:18:08 +00:00
Ariel Rin
ea2b5bfecf Merge branch 'srp-killmail-link-fix-issue-1282' into 'master'
Proper error message on non kill mail zkb links

See merge request allianceauth/allianceauth!1297
2021-04-07 15:12:56 +00:00
Peter Pfeufer
aa7495fa60 Proper error message on non kill mail zkb links 2021-04-07 15:12:56 +00:00
Ariel Rin
162ec1bd86 Merge branch 'docs-permissioncleanup' into 'master'
Permissions Cleanup Docs

See merge request allianceauth/allianceauth!1293
2021-04-07 13:23:28 +00:00
Ariel Rin
2668884008 Merge branch 'srp-copypaste' into 'master'
SRP Character Name Copy-Paste

See merge request allianceauth/allianceauth!1295
2021-04-07 13:22:24 +00:00
Ariel Rin
abdc3f3485 SRP Character Name Copy-Paste 2021-04-07 13:22:23 +00:00
Ariel Rin
911f21ee7c Merge branch 'add-support-discord-link' into 'master'
support discord link added to admin notifications on dashboard

See merge request allianceauth/allianceauth!1298
2021-04-07 13:21:26 +00:00
Ariel Rin
2387c40254 Limit Django to 3.1.x 2021-04-07 13:12:15 +00:00
Peter Pfeufer
76e18a79b3 support discord link added to admin notifications on dashboard 2021-04-06 10:44:12 +02:00
Ariel Rin
9725c9c947 Update .gitlab-ci.yml file 2021-03-27 08:38:40 +00:00
Ariel Rin
247ed7cc64 Merge branch 'nootification_log_spam' into 'master'
Use default by default in Notification system

See merge request allianceauth/allianceauth!1294
2021-02-25 11:04:18 +00:00
Aaron Kable
2fd2af793e Use default by defaut in notification system 2021-02-21 18:14:42 +08:00
Ariel Rin
3fc36b9ce1 Permissions Cleanup Tool 2021-02-18 13:33:23 +10:00
Ariel Rin
c12fd2d7bc Merge branch 'improve_developer_docs' into 'master'
Improve developer docs

See merge request allianceauth/allianceauth!1292
2021-02-18 01:52:15 +00:00
ErikKalkoken
7fe1ba2fb2 Improve docs for WSL setup 2021-02-17 09:50:01 +01:00
ErikKalkoken
ab7eb3e5df Fix sphinx issues in docs 2021-02-17 09:35:18 +01:00
45 changed files with 842 additions and 326 deletions

View File

@@ -1,47 +1,117 @@
stages:
- gitlab
- test
- deploy
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
include:
- template: Dependency-Scanning.gitlab-ci.yml
- template: Security/SAST.gitlab-ci.yml
test-3.6-core:
image: python:3.6-buster
script:
- tox -e py36-core
sast:
stage: gitlab
dependency_scanning:
stage: gitlab
before_script:
- apt-get update && apt-get install redis-server libmariadbclient-dev -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
test-3.7-core:
image: python:3.7-buster
script:
- tox -e py37-core
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.8-core:
image: python:3.8-buster
script:
- tox -e py38-core
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.6-all:
image: python:3.6-buster
test-3.9-core:
image: python:3.9-buster
script:
- tox -e py36-all
- tox -e py39-core
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.7-all:
image: python:3.7-buster
script:
- tox -e py37-all
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.8-all:
image: python:3.8-buster
script:
- tox -e py38-all
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.9-all:
image: python:3.9-buster
script:
- tox -e py39-all
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
deploy_production:
stage: deploy
image: python:3.8-buster
image: python:3.9-buster
before_script:
- pip install twine wheel
@@ -51,4 +121,4 @@ deploy_production:
- twine upload dist/*
rules:
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_TAG

View File

@@ -1,7 +1,7 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
__version__ = '2.8.2'
__version__ = '2.9.0a1'
__title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = '%s v%s' % (__title__, __version__)

View File

@@ -0,0 +1,33 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0017_remove_fleetup_permission'),
]
operations = [
migrations.AlterField(
model_name='characterownership',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='ownershiprecord',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='state',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='userprofile',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('corputils', '0005_cleanup_permissions'),
]
operations = [
migrations.AlterField(
model_name='corpmember',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='corpstats',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eve_autogroups', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='autogroupsconfig',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='managedalliancegroup',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='managedcorpgroup',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eveonline', '0014_auto_20210105_1413'),
]
operations = [
migrations.AlterField(
model_name='eveallianceinfo',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='evecharacter',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='evecorporationinfo',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fleetactivitytracking', '0006_auto_20180803_0430'),
]
operations = [
migrations.AlterField(
model_name='fat',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='fatlink',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -42,6 +42,7 @@
</div>
{% endblock content %}
{% block extra_script %}
$(document).ready(function(){
$("[rel=tooltip]").tooltip();
$(document).ready(function () {
$("[rel=tooltip]").tooltip();
});
{% endblock extra_script %}

View File

@@ -44,6 +44,7 @@
</div>
{% endblock content %}
{% block extra_script %}
$(document).ready(function(){
$("[rel=tooltip]").tooltip();
$(document).ready(function () {
$("[rel=tooltip]").tooltip();
});
{% endblock extra_script %}

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('groupmanagement', '0015_make_descriptions_great_again'),
]
operations = [
migrations.AlterField(
model_name='grouprequest',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='requestlog',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -83,7 +83,7 @@
{% block extra_script %}
$.fn.dataTable.moment = function(format, locale) {
var types = $.fn.dataTable.ext.type;
let types = $.fn.dataTable.ext.type;
// Add type detection
types.detect.unshift(function(d) {

View File

@@ -0,0 +1,43 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hrapplications', '0007_auto_20200918_1412'),
]
operations = [
migrations.AlterField(
model_name='application',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='applicationchoice',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='applicationcomment',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='applicationform',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='applicationquestion',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='applicationresponse',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('notifications', '0004_performance_tuning'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -33,10 +33,8 @@ def user_unread_notification_count(user: object) -> int:
@register.simple_tag
def notifications_refresh_time() -> int:
refresh_time = getattr(settings, 'NOTIFICATIONS_REFRESH_TIME', None)
if (
refresh_time is None or not isinstance(refresh_time, int) or refresh_time < 0
):
refresh_time = getattr(settings, 'NOTIFICATIONS_REFRESH_TIME', Notification.NOTIFICATIONS_REFRESH_TIME_DEFAULT)
if (not isinstance(refresh_time, int) or refresh_time < 0):
logger.warning('NOTIFICATIONS_REFRESH_TIME setting is invalid. Using default.')
refresh_time = Notification.NOTIFICATIONS_REFRESH_TIME_DEFAULT

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('optimer', '0004_on_delete'),
]
operations = [
migrations.AlterField(
model_name='optimer',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -34,17 +34,15 @@
{% endblock %}
{% block extra_script %}
$('#id_start').datetimepicker({
setlocale: '{{ LANGUAGE_CODE }}',
{% if NIGHT_MODE %}
theme: 'dark',
{% else %}
theme: 'default',
{% endif %}
mask: true,
format: 'Y-m-d H:i',
minDate: 0
setlocale: '{{ LANGUAGE_CODE }}',
{% if NIGHT_MODE %}
theme: 'dark',
{% else %}
theme: 'default',
{% endif %}
mask: true,
format: 'Y-m-d H:i',
minDate: 0
});
{% endblock extra_script %}

View File

@@ -41,9 +41,10 @@
{% include 'bundles/moment-js.html' with locale=True %}
<script src="{% static 'js/timers.js' %}"></script>
<script type="application/javascript">
// Data
var timers = [
let timers = [
{% for op in optimer %}
{
'id': {{ op.id }},
@@ -52,67 +53,66 @@
},
{% endfor %}
];
</script>
<script type="application/javascript">
timedUpdate();
setAllLocalTimes();
// Start timed updates
setInterval(timedUpdate, 1000);
function timedUpdate() {
updateClock();
updateAllTimers();
}
function updateAllTimers () {
var l = timers.length;
for (var i=0; i < l; ++i) {
if (timers[i].expired) continue;
updateTimer(timers[i]);
}
}
/**
* Update a timer
* @param timer Timer information
* @param timer.start Date of the timer
* @param timer.id Id number of the timer
* @param timer.expired
*/
function updateTimer(timer) {
let updateTimer = function (timer) {
if (timer.start.isAfter(Date.now())) {
var duration = moment.duration(timer.start - moment(), 'milliseconds');
let duration = moment.duration(timer.start - moment(), 'milliseconds');
document.getElementById("countdown" + timer.id).innerHTML = getDurationString(duration);
} else {
timer.expired = true;
document.getElementById("countdown" + timer.id).innerHTML = "";
}
}
};
let updateAllTimers = function () {
let l = timers.length;
/**
* Set all local time fields
*/
function setAllLocalTimes() {
var l = timers.length;
for (var i=0; i < l; ++i) {
setLocalTime(timers[i]);
if (timers[i].expired) continue;
updateTimer(timers[i]);
}
}
};
/**
* Set the local time info for the timer
* @param timer Timer information
* @param timer.start Date of the timer
* @param timer.id Id number of the timer
*/
function setLocalTime(timer) {
let setLocalTime = function (timer) {
document.getElementById("localtime" + timer.id).innerHTML = timer.start.format("ddd @ LT");
}
};
function updateClock() {
/**
* Set all local time fields
*/
let setAllLocalTimes = function () {
let l = timers.length;
for (var i=0; i < l; ++i) {
setLocalTime(timers[i]);
}
};
let updateClock = function () {
document.getElementById("current-time").innerHTML = getCurrentEveTimeString();
}
};
let timedUpdate = function () {
updateClock();
updateAllTimers();
};
// Set initial values
setAllLocalTimes();
timedUpdate();
// Start timed updates
setInterval(timedUpdate, 1000);
</script>
{% endblock content %}

View File

@@ -24,7 +24,7 @@
</tr>
</thead>
<tbody>
{% for user in permission.users %}
{% for user in permission.users %}
{% include 'permissions_tool/audit_row.html' with type="User" name="Permission granted directlty" %}
{% endfor %}
{% for group in permission.groups %}
@@ -35,13 +35,13 @@
{% for state in permission.states %}
{% for profile in state.userprofile_set.all %}
{% with profile.user as user %}
{% include 'permissions_tool/audit_row.html' with type="State" name=state%}
{% include 'permissions_tool/audit_row.html' with type="State" name=state%}
{% endwith %}
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock content %}
@@ -56,15 +56,16 @@
{% block extra_script %}
$(document).ready(function() {
var groupColumn = 0;
var table = $('#tab_permissions_audit').DataTable({
let groupColumn = 0;
$('#tab_permissions_audit').DataTable({
columnDefs: [
{ "visible": false, "targets": groupColumn }
],
order: [[ groupColumn, 'asc' ], [ 2, 'asc' ] ],
filterDropDown:
{
columns: [
columns: [
{
idx: 0,
title: 'Source'
@@ -73,20 +74,20 @@
bootstrap: true
},
drawCallback: function ( settings ) {
var api = this.api();
var rows = api.rows( {page:'current'} ).nodes();
var last=null;
let api = this.api();
let rows = api.rows( {page:'current'} ).nodes();
let last = null;
api.column(groupColumn, {page:'current'} ).data().each( function ( group, i ) {
if ( last !== group ) {
$(rows).eq( i ).before(
'<tr class="tr-group"><td colspan="3">' + group + '</td></tr>'
);
last = group;
}
} );
}
} );
} );
{% endblock %}
{% endblock %}

View File

@@ -9,9 +9,9 @@
<div class="col-sm-12">
<h1 class="page-header">{% trans "Permissions Overview" %}</h1>
<p>
{% if request.GET.all != 'yes' %}
{% if request.GET.all != 'yes' %}
{% blocktrans %}Showing only applied permissions{% endblocktrans %}
<a href="{% url 'permissions_tool:overview' %}?all=yes" class="btn btn-primary">{% trans "Show All" %}</a>
<a href="{% url 'permissions_tool:overview' %}?all=yes" class="btn btn-primary">{% trans "Show All" %}</a>
{% else %}
{% blocktrans %}Showing all permissions{% endblocktrans %}
<a href="{% url 'permissions_tool:overview' %}?all=no" class="btn btn-primary">{% trans "Show Applied" %}</a>
@@ -79,7 +79,7 @@
{% endblock content %}
{% block extra_javascript %}
{% include 'bundles/datatables-js.html' %}
{% include 'bundles/datatables-js.html' %}
<script type="application/javascript" src="{% static 'js/filterDropDown/filterDropDown.min.js' %}"></script>
{% endblock %}
@@ -89,15 +89,16 @@
{% block extra_script %}
$(document).ready(function() {
var groupColumn = 0;
var table = $('#tab_permissions_overview').DataTable({
let groupColumn = 0;
$('#tab_permissions_overview').DataTable({
columnDefs: [
{ "visible": false, "targets": groupColumn }
],
order: [[ groupColumn, 'asc' ], [ 1, 'asc' ], [ 2, 'asc' ] ],
filterDropDown:
{
columns: [
columns: [
{
idx: 0
},
@@ -108,20 +109,20 @@
bootstrap: true
},
drawCallback: function ( settings ) {
var api = this.api();
var rows = api.rows( {page:'current'} ).nodes();
var last=null;
let api = this.api();
let rows = api.rows( {page:'current'} ).nodes();
let last = null;
api.column(groupColumn, {page:'current'} ).data().each( function ( group, i ) {
if ( last !== group ) {
$(rows).eq( i ).before(
'<tr class="tr-group"><td colspan="6">' + group + '</td></tr>'
);
last = group;
}
} );
}
} );
} );
{% endblock %}
{% endblock %}

View File

@@ -262,3 +262,5 @@ LOGGING = {
},
}
}
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('services', '0003_remove_broken_link'),
]
operations = [
migrations.AlterField(
model_name='nameformatconfig',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -12,20 +12,12 @@ import base64
import hmac
import hashlib
try:
from urllib import unquote, urlencode
except ImportError: #py3
from urllib.parse import unquote, urlencode
try:
from urlparse import parse_qs
except ImportError: #py3
from urllib.parse import parse_qs
from urllib.parse import unquote, urlencode, parse_qs
import logging
logger = logging.getLogger(__name__)
ACCESS_PERM = 'discourse.access_discourse'
@@ -55,7 +47,7 @@ def discourse_sso(request):
# Validate the payload
try:
payload = unquote(payload).encode('utf-8')
decoded = base64.decodestring(payload).decode('utf-8')
decoded = base64.decodebytes(payload).decode('utf-8')
assert 'nonce' in decoded
assert len(payload) > 0
except AssertionError:
@@ -86,7 +78,7 @@ def discourse_sso(request):
if main_char:
params['avatar_url'] = main_char.portrait_url(256)
return_payload = base64.encodestring(urlencode(params).encode('utf-8'))
return_payload = base64.encodebytes(urlencode(params).encode('utf-8'))
h = hmac.new(key, return_payload, digestmod=hashlib.sha256)
query_string = urlencode({'sso': return_payload, 'sig': h.hexdigest()})

View File

@@ -0,0 +1,28 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('teamspeak3', '0005_stategroup'),
]
operations = [
migrations.AlterField(
model_name='authts',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='stategroup',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='usertsgroup',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -1,3 +1,5 @@
import re
from django import forms
from django.utils.translation import ugettext_lazy as _
@@ -21,6 +23,11 @@ class SrpFleetUserRequestForm(forms.Form):
data = self.cleaned_data['killboard_link']
if "zkillboard.com" not in data:
raise forms.ValidationError(_("Invalid Link. Please use zKillboard.com"))
if not re.match(r"http[s]?://zkillboard\.com/kill/\d+\/", data):
raise forms.ValidationError(
_("Invalid Link. Please post a direct link to a killmail.")
)
return data

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('srp', '0004_on_delete'),
]
operations = [
migrations.AlterField(
model_name='srpfleetmain',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='srpuserrequest',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -43,17 +43,15 @@
{% endblock %}
{% block extra_script %}
$('#id_fleet_time').datetimepicker({
setlocale: '{{ LANGUAGE_CODE }}',
{% if NIGHT_MODE %}
theme: 'dark',
{% else %}
theme: 'default',
{% endif %}
mask: true,
format: 'Y-m-d H:i',
minDate: 0
setlocale: '{{ LANGUAGE_CODE }}',
{% if NIGHT_MODE %}
theme: 'dark',
{% else %}
theme: 'default',
{% endif %}
mask: true,
format: 'Y-m-d H:i',
minDate: 0
});
{% endblock extra_script %}

View File

@@ -10,6 +10,9 @@
{% include 'bundles/x-editable.css.html' %}
<link href="{% static 'css/checkbox.css' %}" rel="stylesheet" type="text/css">
<style>
.copy-text-fa-icon:hover {
cursor: pointer;
}
.radio label, .checkbox label {
padding-left: 10px;
}
@@ -109,7 +112,8 @@ ESC to cancel{% endblocktrans %}"id="blah"></i></th>
{{ srpfleetrequest.character.alliance.alliance_ticker }}
{% endif %}
[{{ srpfleetrequest.character.corporation.corporation_ticker }}]
{{ srpfleetrequest.character.character_name }}
{{ srpfleetrequest.character.character_name }}&nbsp;<i class="copy-text-fa-icon far fa-copy" data-clipboard-text="{{ srpfleetrequest.character.character_name }}"></i>
</span>
</td>
<td class="text-center">
<a href="{{ srpfleetrequest.killboard_link }}"
@@ -182,76 +186,91 @@ ESC to cancel{% endblocktrans %}"id="blah"></i></th>
{% include 'bundles/datatables-js.html' %}
{% include 'bundles/x-editable-js.html' %}
{% include 'bundles/moment-js.html' %}
{% endblock %}
{% include 'bundles/clipboard-js.html' %}
<script>
var clipboard = new ClipboardJS('.copy-text-fa-icon');
clipboard.on('success', function (e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
e.clearSelection();
});
clipboard.on('error', function (e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
</script>
{% endblock extra_javascript %}
{% block extra_script %}
$(document).ready(function() {
$.fn.editable.defaults.mode = 'inline';
$.fn.editable.defaults.showbuttons = false;
$.fn.editable.defaults.highlight = "#AAFF80";
$(document).ready(function() {
$.fn.editable.defaults.mode = 'inline';
$.fn.editable.defaults.showbuttons = false;
$.fn.editable.defaults.highlight = "#AAFF80";
$.fn.dataTable.moment = function(format, locale) {
let types = $.fn.dataTable.ext.type;
$('.srp').editable({
display: function(value, response) {
return false;
},
success: function(response, newValue) {
newValue = parseInt(newValue);
newvalue = newValue.toLocaleString() + " ISK";
$(this).html(newvalue.bold());
},
validate: function(value) {
if (value === null || value === '') {
return 'Empty values not allowed';
}
}
});
$('.srp').on('hidden', function(e, reason){
if(reason === 'save' || reason === 'nochange') {
var $next = $(this).closest('tr').next().find('.editable');
setTimeout(function() {
$next.editable('show');
}, 400);
}
});
});
// Add type detection
types.detect.unshift(function(d) {
return moment(d, format, locale, true).isValid() ?
'moment-' + format :
null;
});
$(document).ready(function(){
$("[rel=tooltip]").tooltip({ placement: 'top'});
});
// Add sorting method - use an integer for the sorting
types.order[ 'moment-' + format+'-pre' ] = function(d) {
return moment(d, format, locale, true).unix();
};
};
$.fn.dataTable.moment('YYYY-MMM-D, HH:mm');
$.fn.dataTable.moment = function(format, locale) {
var types = $.fn.dataTable.ext.type;
$('.srp').editable({
display: function(value, response) {
return false;
},
success: function(response, newValue) {
newValue = parseInt(newValue);
let newValueOutput = newValue.toLocaleString() + " ISK";
// Add type detection
types.detect.unshift(function(d) {
return moment(d, format, locale, true).isValid() ?
'moment-'+format :
null;
} );
$(this).html(newValueOutput.bold());
},
validate: function(value) {
if (value === null || value === '') {
return 'Empty values not allowed';
}
}
});
// Add sorting method - use an integer for the sorting
types.order[ 'moment-'+format+'-pre' ] = function(d) {
return moment(d, format, locale, true).unix();
};
};
$('.srp').on('hidden', function(e, reason){
if(reason === 'save' || reason === 'nochange') {
let $next = $(this).closest('tr').next().find('.editable');
$(document).ready( function(){
$.fn.dataTable.moment('YYYY-MMM-D, HH:mm');
setTimeout(function() {
$next.editable('show');
}, 400);
}
});
$('table.srplist').DataTable({
"order": [[ 6, "asc" ]],
"paging": false,
"columnDefs": [{
"targets": [1, 8],
"orderable": false
},
{
"targets": [4, 5],
"type": "num"
}]
$('table.srplist').DataTable({
"order": [[ 6, "asc" ]],
"paging": false,
"columnDefs": [
{
"targets": [1, 8],
"orderable": false
},
{
"targets": [4, 5],
"type": "num"
}
]
});
// tooltip
$("[rel=tooltip]").tooltip({ placement: 'top'});
});
});
{% endblock extra_script %}

View File

@@ -1,3 +1,5 @@
/* global notificationUPdateSettings */
/*
This script refreshed the unread notification count in the top menu
on a regular basis so to keep the user apprised about newly arrived
@@ -6,70 +8,67 @@
The refresh rate can be changes via the Django setting NOTIFICATIONS_REFRESH_TIME.
See documentation for details.
*/
$(function () {
var elem = document.getElementById("dataExport");
var notificationsListViewUrl = elem.getAttribute("data-notificationsListViewUrl");
var notificationsRefreshTime = elem.getAttribute("data-notificationsRefreshTime");
var userNotificationsCountViewUrl = elem.getAttribute(
"data-userNotificationsCountViewUrl"
);
'use strict';
let notificationsListViewUrl = notificationUPdateSettings.notificationsListViewUrl;
let notificationsRefreshTime = notificationUPdateSettings.notificationsRefreshTime;
let userNotificationsCountViewUrl = notificationUPdateSettings.userNotificationsCountViewUrl;
// update the notification unread count in the top menu
function update_notifications() {
let updateNotifications = function () {
$.getJSON(userNotificationsCountViewUrl, function (data, status) {
if (status == 'success') {
var innerHtml = "";
var unread_count = data.unread_count;
if (unread_count > 0) {
if (status === 'success') {
let innerHtml = '';
let unreadCount = data.unread_count;
if (unreadCount > 0) {
innerHtml = (
`Notifications <span class="badge">${unread_count}</span>`
)
`Notifications <span class="badge">${unreadCount}</span>`
);
} else {
innerHtml = '<i class="far fa-bell"></i>';
}
else {
innerHtml = '<i class="far fa-bell"></i>'
}
$("#menu_item_notifications").html(
$('#menu_item_notifications').html(
`<a href="${notificationsListViewUrl}">${innerHtml}</a>`
);
}
else {
} else {
console.error(
`Failed to load HTMl to render notifications item. Error: `
`${xhr.status}': '${xhr.statusText}`
`Failed to load HTMl to render notifications item. Error: ${xhr.status}': '${xhr.statusText}`
);
}
});
}
};
var myInterval;
let myInterval;
// activate automatic refreshing every x seconds
function activate_refreshing() {
let activateRefreshing = function () {
if (notificationsRefreshTime > 0) {
myInterval = setInterval(
update_notifications, notificationsRefreshTime * 1000
updateNotifications, notificationsRefreshTime * 1000
);
}
}
};
// deactivate automatic refreshing
function deactivate_refreshing() {
let deactivateRefreshing = function () {
if ((notificationsRefreshTime > 0) && (typeof myInterval !== 'undefined')) {
clearInterval(myInterval)
clearInterval(myInterval);
}
}
};
// refreshing only happens on active browser tab
$(document).on({
'show': function () {
activate_refreshing()
activateRefreshing();
},
'hide': function () {
deactivate_refreshing()
deactivateRefreshing();
}
});
// Initial start of refreshing on script loading
activate_refreshing()
activateRefreshing();
});

View File

@@ -1,23 +1,49 @@
/* global moment */
/**
* Get a duration string like countdown.js
* e.g. "1y 2d 3h 4m 5s"
* @param duration moment.duration
*/
function getDurationString(duration) {
var out = "";
* Get a duration string like countdown.js
* e.g. "1y 2d 3h 4m 5s"
*
* @param duration
* @returns {string}
*/
let getDurationString = function (duration) {
'use strict';
let out = '';
if (duration.years()) {
out += duration.years() + 'y ';
}
if (duration.months()) {
out += duration.months() + 'm ';
}
if (duration.days()) {
out += duration.days() + 'd ';
}
return out + duration.hours() + "h " + duration.minutes() + "m " + duration.seconds() + "s";
}
return out + duration.hours() + 'h ' + duration.minutes() + 'm ' + duration.seconds() + 's';
};
function getCurrentEveTimeString() {
return moment().utc().format('dddd LL HH:mm:ss')
}
/**
* returns the current eve time as a formatted string
*
* condition:
* only if moment.js is loaded before,
* if not this function returns an empty string to avoid JS errors from happening.
*
* @returns {string}
*/
let getCurrentEveTimeString = function () {
'use strict';
let returnValue = '';
if (window.moment) {
returnValue = moment().utc().format('dddd LL HH:mm:ss');
}
return returnValue;
};

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@@ -18,9 +18,20 @@
{% endfor %}
</ul>
</div>
<div class="text-right" style="position:absolute;bottom:5px;right:5px;">
<a href="https://gitlab.com/allianceauth/allianceauth/issues"><span class="label" style="background-color:#e65328;">
<i class="fab fa-gitlab" aria-hidden="true"></i> Powered by GitLab</span>
<div class="text-right" style="position: absolute; bottom: 5px; right: 5px;">
<a href="https://gitlab.com/allianceauth/allianceauth/issues" target="_blank" style="margin-right: 0.5rem;">
<span class="label" style="background-color: #e65328;">
<i class="fab fa-gitlab" aria-hidden="true"></i>
{% translate 'Powered by GitLab' %}
</span>
</a>
<a href="https://discord.com/invite/fjnHAmk" target="_blank">
<span class="label" style="background-color: rgb(110,133,211);">
<i class="fab fa-discord" aria-hidden="true"></i>
{% translate 'Support Discord' %}
</span>
</a>
</div>
</div>

View File

@@ -46,19 +46,20 @@
</div>
</div>
{% endif %}
<!-- share data with JS part -->
<div
id="dataExport"
data-notificationsListViewUrl="{% url 'notifications:list' %}"
data-notificationsRefreshTime="{% notifications_refresh_time %}"
data-userNotificationsCountViewUrl="{% url 'notifications:user_notifications_count' request.user.pk %}"
>
</div>
{% include 'bundles/bootstrap-js.html' %}
{% include 'bundles/jquery-visibility-js.html' %}
<script type="application/javascript">
let notificationUPdateSettings = {
notificationsListViewUrl: "{% url 'notifications:list' %}",
notificationsRefreshTime: "{% notifications_refresh_time %}",
userNotificationsCountViewUrl: "{% url 'notifications:user_notifications_count' request.user.pk %}"
};
</script>
<script src="{% static 'js/refresh_notifications.js' %}"></script>
{% block extra_javascript %}
{% block extra_javascript %}
{% endblock extra_javascript %}
<script>
{% block extra_script %}

View File

@@ -1,3 +1,3 @@
<!-- Start Clipboard.js js from cdnjs -->
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.6/clipboard.min.js"></script>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.8/clipboard.min.js" integrity="sha512-sIqUEnRn31BgngPmHt2JenzleDDsXwYO+iyvQ46Mw6RL+udAUZj2n/u/PGY80NxRxynO7R9xIGx5LEzw4INWJQ==" crossorigin="anonymous"></script>
<!-- End Clipboard.js js from cdnjs -->

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('timerboard', '0003_on_delete'),
]
operations = [
migrations.AlterField(
model_name='timer',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -526,9 +526,7 @@
{% include 'bundles/moment-js.html' with locale=True %}
<script src="{% static 'js/timers.js' %}"></script>
<script type="application/javascript">
var locale = "{{ LANGUAGE_CODE }}";
var timers = [
let timers = [
{% for timer in timers %}
{
'id': {{ timer.id }},
@@ -545,67 +543,64 @@
{% endfor %}
];
moment.locale(locale);
/**
* Update a timer
* @param timer Timer information
*/
let updateTimer = function (timer) {
if (timer.targetDate.isAfter(Date.now())) {
let duration = moment.duration(timer.targetDate - moment(), 'milliseconds');
// Set initial values
setAllLocalTimes();
timedUpdate();
document.getElementById("countdown" + timer.id).innerHTML = getDurationString(duration);
} else {
timer.expired = true;
// Start timed updates
setInterval(timedUpdate, 1000);
document.getElementById("countdown" + timer.id).innerHTML = "";
}
};
let updateAllTimers = function () {
let l = timers.length;
for (var i=0; i < l; ++i) {
if (timers[i].expired) continue;
updateTimer(timers[i]);
}
};
function timedUpdate() {
updateClock();
updateAllTimers();
}
function updateAllTimers () {
var l = timers.length;
for (var i=0; i < l; ++i) {
if (timers[i].expired) continue;
updateTimer(timers[i]);
}
}
/**
* Update a timer
* Set the local time info for the timer
* @param timer Timer information
* @param timer.targetDate Date of the timer
* @param timer.id Id number of the timer
* @param timer.expired
*/
function updateTimer(timer) {
if (timer.targetDate.isAfter(Date.now())) {
duration = moment.duration(timer.targetDate - moment(), 'milliseconds');
document.getElementById("countdown" + timer.id).innerHTML = getDurationString(duration);
} else {
timer.expired = true;
document.getElementById("countdown" + timer.id).innerHTML = "";
}
}
let setLocalTime = function (timer) {
document.getElementById("localtime" + timer.id).innerHTML = timer.targetDate.format("ddd @ LT");
};
/**
* Set all local time fields
*/
function setAllLocalTimes() {
var l = timers.length;
let setAllLocalTimes = function () {
let l = timers.length;
for (var i=0; i < l; ++i) {
setLocalTime(timers[i]);
}
}
};
/**
* Set the local time info for the timer
* @param timer Timer information
* @param timer.targetDate Date of the timer
* @param timer.id Id number of the timer
*/
function setLocalTime(timer) {
document.getElementById("localtime" + timer.id).innerHTML = timer.targetDate.format("ddd @ LT");
}
function updateClock() {
let updateClock = function () {
document.getElementById("current-time").innerHTML = getCurrentEveTimeString();
}
};
// Set initial values
setAllLocalTimes();
timedUpdate();
// Start timed updates
setInterval(timedUpdate, 1000);
</script>
{% endblock content %}

View File

@@ -25,6 +25,11 @@ The development environment consists of the following components:
We will use the build-in Django development webserver, so we don't need to setup a WSGI server or a web server.
```eval_rst
.. note::
This setup works with both WSL 1 and WSL 2. However, due to the significantly better performance we recommend WSL 2.
```
## Requirement
The only requirement is a PC with Windows 10 and Internet connection in order to download the additional software components.
@@ -361,8 +366,7 @@ Here is an example debug config for Celery:
"module": "celery",
"cwd": "${workspaceFolder}/myauth",
"console": "integratedTerminal",
"args": [
"-E",
"args": [
"-A",
"myauth",
"worker",
@@ -371,7 +375,8 @@ Here is an example debug config for Celery:
"-P",
"solo",
],
"django": true
"django": true,
"justMyCode": true,
},
```
@@ -386,15 +391,14 @@ Finally it makes sense to have a dedicated debug config for running unit tests.
"request": "launch",
"program": "${workspaceFolder}/myauth/manage.py",
"args": [
"test",
"-v 2",
"test",
"--keepdb",
"--debug-mode",
"--failfast",
"example",
],
"django": true
"django": true,
"justMyCode": true
},
```
@@ -416,13 +420,31 @@ Finally you may also want to have a debug config to debug a non-Django Python sc
## Additional tools
The following additional tools are very helpful when developing for AA.
The following additional tools are very helpful when developing for AA with VS Code:
### Pylance
Pylance is an extension that works alongside Python in Visual Studio Code to provide performant language support: [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance)
### Code Spell Checker
Typos in your user facing comments can be quite embarrassing. This spell checker helps you avoid them.
Typos in your user facing comments can be quite embarrassing. This spell checker helps you avoid them: [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
Install from here: [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
### markdownlint
Extension for Visual Studio Code - Markdown linting and style checking for Visual Studio Code: [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint)
### GitLens
Extension for Visual Studio Code - Supercharge the Git capabilities built into Visual Studio Code: [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
### RST preview
A VS Code extension to preview restructured text and provide syntax highlighting: [RST Preview](https://marketplace.visualstudio.com/items?itemName=tht13.rst-vscode)
### Django Template
This extension adds language colorization support and user snippets for the Django template language to VS Code: [Django Template](https://marketplace.visualstudio.com/items?itemName=bibhasdn.django-html)
### DBeaver

View File

@@ -14,7 +14,7 @@ models
===========
.. autoclass:: allianceauth.notifications.models.Notification
:members: view, set_level, LEVEL_CHOICES
:members: LEVEL_CHOICES, mark_viewed, set_level
:undoc-members:
managers

View File

@@ -42,11 +42,12 @@ Auto Groups are configured via models in the Admin Interface, a user will requir
+-------------------------------------------+------------------+----------------+
| Permission | Admin Site | Auth Site |
+===========================================+==================+================+
| eve_autogroups.add_autogroupsconfig | Can create model | None. |
| eve_autogroups.add_autogroupsconfig | Can create model | None. |
+-------------------------------------------+------------------+----------------+
| eve_autogroups.change_autogroupsconfig | Can edit model | None. |
| eve_autogroups.change_autogroupsconfig | Can edit model | None. |
+-------------------------------------------+------------------+----------------+
| eve_autogroups.delete_autogroupsconfig | Can delete model | None. |
| eve_autogroups.delete_autogroupsconfig | Can delete model | None. |
+-------------------------------------------+------------------+----------------+
```
There exists more models that will be automatically created and maintained by this module, they do not require end-user/admin interaction. `managedalliancegroup` `managedcorpgroups`

View File

@@ -38,14 +38,14 @@ To install we need a copy of the server. You can find the latest version from [t
Download the server, replacing the link with the link you got earlier.
```url
```text
http://dl.4players.de/ts/releases/3.13.2/teamspeak3-server_linux_amd64-3.13.2.tar.bz2
```
Now we need to extract the file.
```bash
tar -xf teamspeak3-server_linux_amd64-3.1.0.tar.bz2
tar -xf teamspeak3-server_linux_amd64-3.1.0.tar.bz2
```
### Create User

View File

@@ -45,17 +45,25 @@ Place your virtual host configuration in the appropriate section within `/etc/ht
```
<VirtualHost *:80>
ServerName auth.example.com
ProxyPassMatch ^/static !
ProxyPassMatch ^/robots.txt !
ProxyPass / http://127.0.0.1:8000/
ProxyPassReverse / http://127.0.0.1:8000/
ProxyPreserveHost On
Alias "/static" "/var/www/myauth/static"
Alias "/robots.txt" "/var/www/myauth/static/robots.txt"
<Directory "/var/www/myauth/static">
Require all granted
</Directory>
<Location "/robots.txt">
SetHandler None
Require all granted
</Location>
</VirtualHost>
```

View File

@@ -69,6 +69,10 @@ server {
autoindex off;
}
location /robots.txt {
alias /var/www/myauth/static/robots.txt;
}
# Gunicorn config goes below
location / {
include proxy_params;

View File

@@ -18,12 +18,17 @@ To run AA with a newer Python 3 version than your system's default you need to i
```eval_rst
.. note::
For stability and performance we currently recommend to run AA with Python 3.7. Since at the time of writing Python 3.7 was not available for CentOS through yum install this guide will upgrade to Python 3.6. For Ubuntu one can just replace "3.6" with "3.7" in the installation commands to get Python 3.7.
For stability and performance we currently recommend to run AA with Python 3.7. It has proven to be the fastest and most stable version in use currently.
```
To install other Python versions than come with your distro you need to add a new installation repository. Then you can install the specific Python 3 to your system.
To install other Python versions than those included with your distribution, you need to add a new installation repository. Then you can install the specific Python 3 to your system.
Ubuntu:
Ubuntu 1604 1804:
```eval_rst
.. note::
Ubuntu 2004 ships with Python 3.8, No updates required.
```
```bash
add-apt-repository ppa:deadsnakes/ppa
@@ -34,23 +39,38 @@ apt-get update
```
```bash
apt-get install python3.6 python3.6-dev python3.6-venv
apt-get install python3.7 python3.7-dev python3.7-venv
```
CentOS:
CentOS 7/8:
```bash
yum install https://centos7.iuscommunity.org/ius-release.rpm
cd ~
```
```bash
yum update
sudo yum install gcc openssl-devel bzip2-devel libffi-devel wget
```
```bash
yum install python36u python36u-pip python36u-devel
wget https://www.python.org/ftp/python/3.7.10/Python-3.7.10.tgz
```
```bash
tar xvf Python-3.7.10.tgz
```
```bash
cd Python-3.7.10/
```
```bash
./configure --enable-optimizations --enable-shared
```
```bash
make altinstall
```
## Preparing your venv
Before updating your venv it is important to make sure that your current installation is stable. Otherwise your new venv might not be consistent with your data, which might create problems.
@@ -97,12 +117,6 @@ If you unsure which apps you have installed from repos check `INSTALLED_APPS` in
pip list
```
Some AA installations might still be running an older version of django-celery-beat. We would recommend to upgrade to the current version before doing the Python update:
```bash
pip install -U 'django-celery-beat<2.00'
```
```bash
python manage.py migrate
```
@@ -171,7 +185,7 @@ mv /home/allianceserver/venv/auth /home/allianceserver/venv/auth_old
Now let's create our new venv with Python 3.6 and activate it:
```bash
python3.6 -m venv /home/allianceserver/venv/auth
python3.7 -m venv /home/allianceserver/venv/auth
```
```bash

View File

@@ -1,4 +1,6 @@
# Adding and Removing Apps
# App Maintenance
## Adding and Removing Apps
Your auth project is just a regular Django project - you can add in [other Django apps](https://djangopackages.org/) as desired. Most come with dedicated setup guides, but here is the general procedure:
@@ -8,3 +10,17 @@ Your auth project is just a regular Django project - you can add in [other Djang
4. restart AA with `supervisorctl restart myauth:`
If you ever want to remove an app, you should first clear it from the database to avoid dangling foreign keys: `python manage.py migrate appname zero`. Then you can remove it from your auth project's `INSTALLED_APPS` list.
## Permission Cleanup
Mature Alliance Auth installations, or those with actively developed extensions may find themselves with stale or duplicated Permission models.
This can make it confusing for admins to apply the right permissions, contribute to larger queries in backend management or simply look unsightly.
```python
python manage.py remove_stale_contenttypes --include-stale-apps
```
This inbuilt Django command will step through each contenttype and offer to delete it, displaying what exactly this will cascade to delete. Pay attention and ensure you understand exactly what is being removed before answering `yes`.
This should only cleanup uninstalled apps, deprecated permissions within apps should be cleaned up using Data Migrations by each responsible application.

View File

@@ -58,7 +58,7 @@ setup(
extras_require={
'testing': testing_extras
},
python_requires='~=3.6',
python_requires='~=3.7',
license='GPLv2',
packages=['allianceauth'],
url=allianceauth.__url__,
@@ -71,16 +71,21 @@ setup(
classifiers=[
'Environment :: Web Environment',
'Framework :: Django',
'Framework :: Django :: 2.2',
'Framework :: Django :: 3.1',
'Framework :: Django :: 3.2',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
],
project_urls={
'Documentation': 'https://allianceauth.readthedocs.io/',
},
)

View File

@@ -1,16 +1,16 @@
[tox]
skipsdist = true
usedevelop = true
envlist = py{36,37,38}-{all}
envlist = py{37,38,39}-{all}
[testenv]
setenv =
all: DJANGO_SETTINGS_MODULE = tests.settings_all
core: DJANGO_SETTINGS_MODULE = tests.settings_core
basepython =
py36: python3.6
py37: python3.7
py38: python3.8
py39: python3.9
deps=
coverage
install_command = pip install -e ".[testing]" -U {opts} {packages}