mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 14:16:21 +01:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f320bc256 | ||
|
|
45b8d42b8e | ||
|
|
bd2d19f867 | ||
|
|
0be404baca | ||
|
|
e6cee9ac83 | ||
|
|
d173a59441 | ||
|
|
8f59f2549a | ||
|
|
630400fee4 | ||
|
|
1ec6929e91 | ||
|
|
8c6bdd8ae2 | ||
|
|
e04138bced | ||
|
|
db5ad85811 | ||
|
|
5b44fd376d | ||
|
|
0036e8b280 | ||
|
|
ea2b5bfecf | ||
|
|
aa7495fa60 | ||
|
|
162ec1bd86 | ||
|
|
2668884008 | ||
|
|
abdc3f3485 | ||
|
|
911f21ee7c | ||
|
|
2387c40254 | ||
|
|
76e18a79b3 | ||
|
|
9725c9c947 | ||
|
|
247ed7cc64 | ||
|
|
2fd2af793e | ||
|
|
3fc36b9ce1 | ||
|
|
c12fd2d7bc | ||
|
|
7fe1ba2fb2 | ||
|
|
ab7eb3e5df |
100
.gitlab-ci.yml
100
.gitlab-ci.yml
@@ -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
|
||||
@@ -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__)
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
23
allianceauth/corputils/migrations/0006_django_3_2_compat.py
Normal file
23
allianceauth/corputils/migrations/0006_django_3_2_compat.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
28
allianceauth/eveonline/migrations/0015_django_3_2_compat.py
Normal file
28
allianceauth/eveonline/migrations/0015_django_3_2_compat.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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
|
||||
|
||||
|
||||
18
allianceauth/optimer/migrations/0005_django_3_2_compat.py
Normal file
18
allianceauth/optimer/migrations/0005_django_3_2_compat.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -262,3 +262,5 @@ LOGGING = {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
18
allianceauth/services/migrations/0004_django_3_2_compat.py
Normal file
18
allianceauth/services/migrations/0004_django_3_2_compat.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -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()})
|
||||
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
23
allianceauth/srp/migrations/0005_django_3_2_compat.py
Normal file
23
allianceauth/srp/migrations/0005_django_3_2_compat.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 }} <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 %}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
2
allianceauth/static/robots.txt
Normal file
2
allianceauth/static/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 -->
|
||||
18
allianceauth/timerboard/migrations/0004_django_3_2_compat.py
Normal file
18
allianceauth/timerboard/migrations/0004_django_3_2_compat.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
13
setup.py
13
setup.py
@@ -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/',
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
4
tox.ini
4
tox.ini
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user