mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-12-06 21:01:42 +01:00
Compare commits
1 Commits
1f6cc8b08f
...
d15913f24c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d15913f24c |
@ -52,10 +52,4 @@ class UserSettingsMiddleware(MiddlewareMixin):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
|
||||||
# Minimize Menu
|
|
||||||
try:
|
|
||||||
request.session["MINIMIZE_SIDEBAR"] = request.user.profile.minimize_sidebar
|
|
||||||
except Exception as e:
|
|
||||||
pass # We don't care that an anonymous user has no profile (not logged in)
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
# Generated by Django 4.2.25 on 2025-10-14 22:02
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentication", "0024_alter_userprofile_language"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="userprofile",
|
|
||||||
name="minimize_sidebar",
|
|
||||||
field=models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Keep the sidebar menu minimized",
|
|
||||||
verbose_name="Minimize Sidebar Menu",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -97,8 +97,7 @@ class UserProfile(models.Model):
|
|||||||
on_delete=models.SET_DEFAULT,
|
on_delete=models.SET_DEFAULT,
|
||||||
default=get_guest_state_pk)
|
default=get_guest_state_pk)
|
||||||
language = models.CharField(
|
language = models.CharField(
|
||||||
_("Language"),
|
_("Language"), max_length=10,
|
||||||
max_length=10,
|
|
||||||
choices=Language.choices,
|
choices=Language.choices,
|
||||||
blank=True,
|
blank=True,
|
||||||
default='')
|
default='')
|
||||||
@ -113,12 +112,6 @@ class UserProfile(models.Model):
|
|||||||
null=True,
|
null=True,
|
||||||
help_text="Bootstrap 5 Themes from https://bootswatch.com/ or Community Apps"
|
help_text="Bootstrap 5 Themes from https://bootswatch.com/ or Community Apps"
|
||||||
)
|
)
|
||||||
minimize_sidebar = models.BooleanField(
|
|
||||||
_("Minimize Sidebar Menu"),
|
|
||||||
default=False,
|
|
||||||
help_text=_("Keep the sidebar menu minimized")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def assign_state(self, state=None, commit=True):
|
def assign_state(self, state=None, commit=True):
|
||||||
if not state:
|
if not state:
|
||||||
|
|||||||
@ -27,7 +27,7 @@ def dashboard_results(hours: int) -> _TaskCounts:
|
|||||||
my_earliest = events.first_event(earliest=earliest)
|
my_earliest = events.first_event(earliest=earliest)
|
||||||
return [my_earliest] if my_earliest else []
|
return [my_earliest] if my_earliest else []
|
||||||
|
|
||||||
earliest = dt.datetime.now(dt.timezone.utc) - dt.timedelta(hours=hours)
|
earliest = dt.datetime.utcnow() - dt.timedelta(hours=hours)
|
||||||
earliest_events = []
|
earliest_events = []
|
||||||
succeeded_count = succeeded_tasks.count(earliest=earliest)
|
succeeded_count = succeeded_tasks.count(earliest=earliest)
|
||||||
earliest_events += earliest_if_exists(succeeded_tasks, earliest)
|
earliest_events += earliest_if_exists(succeeded_tasks, earliest)
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class EventSeries:
|
|||||||
- event_time: timestamp of event. Will use current time if not specified.
|
- event_time: timestamp of event. Will use current time if not specified.
|
||||||
"""
|
"""
|
||||||
if not event_time:
|
if not event_time:
|
||||||
event_time = dt.datetime.now(dt.timezone.utc)
|
event_time = dt.datetime.utcnow()
|
||||||
my_id = self._redis.incr(self._key_counter)
|
my_id = self._redis.incr(self._key_counter)
|
||||||
self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()})
|
self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()})
|
||||||
|
|
||||||
|
|||||||
@ -88,7 +88,6 @@ class TestUserSettingsMiddlewareLoginFlow(TestCase):
|
|||||||
self.request.LANGUAGE_CODE = 'en'
|
self.request.LANGUAGE_CODE = 'en'
|
||||||
self.request.user.profile.language = 'de'
|
self.request.user.profile.language = 'de'
|
||||||
self.request.user.profile.night_mode = True
|
self.request.user.profile.night_mode = True
|
||||||
self.request.user.profile.minimize_sidebar = False
|
|
||||||
self.request.user.is_anonymous = False
|
self.request.user.is_anonymous = False
|
||||||
self.response = Mock()
|
self.response = Mock()
|
||||||
self.response.content = 'hello world'
|
self.response.content = 'hello world'
|
||||||
@ -174,26 +173,3 @@ class TestUserSettingsMiddlewareLoginFlow(TestCase):
|
|||||||
self.response
|
self.response
|
||||||
)
|
)
|
||||||
self.assertEqual(self.request.session["NIGHT_MODE"], True)
|
self.assertEqual(self.request.session["NIGHT_MODE"], True)
|
||||||
|
|
||||||
def test_middleware_set_mimimize_sidebar(self):
|
|
||||||
"""
|
|
||||||
tests the middleware will always set minimize_sidebar to False (default)
|
|
||||||
"""
|
|
||||||
|
|
||||||
response = self.middleware.process_response(
|
|
||||||
self.request,
|
|
||||||
self.response
|
|
||||||
)
|
|
||||||
self.assertEqual(self.request.session["MINIMIZE_SIDEBAR"], False)
|
|
||||||
|
|
||||||
def test_middleware_minimize_sidebar_when_set(self):
|
|
||||||
"""
|
|
||||||
tests the middleware will set mimimize_sidebar to True from DB
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.request.user.profile.minimize_sidebar = True
|
|
||||||
response = self.middleware.process_response(
|
|
||||||
self.request,
|
|
||||||
self.response
|
|
||||||
)
|
|
||||||
self.assertEqual(self.request.session["MINIMIZE_SIDEBAR"], True)
|
|
||||||
|
|||||||
@ -72,31 +72,6 @@
|
|||||||
|
|
||||||
{% theme_select %}
|
{% theme_select %}
|
||||||
|
|
||||||
{% if user.is_authenticated and not request.is_mobile_device %}
|
|
||||||
<li><hr class="dropdown-divider"></li>
|
|
||||||
<li><h6 class="dropdown-header">{% translate "Sidebar" %}</h6></li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<form class="dropdown-item" action="{% url 'minimize_sidebar' %}?next={{ request.path|urlencode }}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
<div class="form-check form-switch">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
role="switch"
|
|
||||||
id="toggle-sidebar"
|
|
||||||
onchange="this.form.submit()"
|
|
||||||
{% if request.session.MINIMIZE_SIDEBAR %}checked{% endif %}
|
|
||||||
>
|
|
||||||
<label class="form-check-label" for="toggle-sidebar">
|
|
||||||
{% translate "Minimize Sidebar" %}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if user.is_superuser %}
|
{% if user.is_superuser %}
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li><h6 class="dropdown-header">{% translate "Super User" %}</h6></li>
|
<li><h6 class="dropdown-header">{% translate "Super User" %}</h6></li>
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
{% load menu_menu_items %}
|
{% load menu_menu_items %}
|
||||||
|
|
||||||
<div class="col-auto px-0">
|
<div class="col-auto px-0">
|
||||||
<div class="collapse collapse-horizontal {% if user.is_authenticated and not request.is_mobile_device and not request.session.MINIMIZE_SIDEBAR %}show{% endif %}" tabindex="-1" id="sidebar">
|
<div class="collapse collapse-horizontal" tabindex="-1" id="sidebar">
|
||||||
<div>
|
<div>
|
||||||
<div class="nav-padding navbar-dark text-bg-dark px-0 d-flex flex-column overflow-hidden vh-100 {% if not user.is_authenticated %}position-relative{% endif %}">
|
<div class="nav-padding navbar-dark text-bg-dark px-0 d-flex flex-column overflow-hidden vh-100 {% if not user.is_authenticated %}position-relative{% endif %}">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
"""
|
|
||||||
Alliance Auth Middleware
|
|
||||||
"""
|
|
||||||
|
|
||||||
from user_agents import parse
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceDetectionMiddleware:
|
|
||||||
"""
|
|
||||||
Middleware to detect the type of device making the request.
|
|
||||||
Sets flags on the request object for easy access in views and templates.
|
|
||||||
|
|
||||||
Flags include:
|
|
||||||
- is_mobile: True if the device is a mobile phone.
|
|
||||||
- is_tablet: True if the device is a tablet.
|
|
||||||
- is_mobile_device: True if the device is either a mobile phone or a tablet.
|
|
||||||
- is_touch_capable: True if the device has touch capabilities.
|
|
||||||
- is_pc: True if the device is a desktop or laptop computer.
|
|
||||||
- is_bot: True if the device is identified as a bot or crawler.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, get_response):
|
|
||||||
"""
|
|
||||||
Initialize the middleware with the get_response callable.
|
|
||||||
|
|
||||||
:param get_response:
|
|
||||||
:type get_response:
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.get_response = get_response
|
|
||||||
|
|
||||||
def __call__(self, request):
|
|
||||||
"""
|
|
||||||
Process the incoming request to determine if it's from a mobile device.
|
|
||||||
|
|
||||||
This method is called when the middleware is invoked. It inspects the
|
|
||||||
`user-agent` header of the incoming HTTP request to determine the type
|
|
||||||
of client making the request (e.g., mobile, tablet, PC, bot, etc.).
|
|
||||||
Flags are set on the `request` object to indicate the client type.
|
|
||||||
|
|
||||||
:param request: The HTTP request object.
|
|
||||||
:type request: HttpRequest
|
|
||||||
:return: The HTTP response object after processing the request.
|
|
||||||
:rtype: HttpResponse
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Retrieve the user-agent string from the request headers
|
|
||||||
user_agent_string = request.headers.get("user-agent", "")
|
|
||||||
|
|
||||||
# Parse the user-agent string to extract client information
|
|
||||||
user_agent = parse(user_agent_string)
|
|
||||||
|
|
||||||
# Set flags on the request object based on the client type
|
|
||||||
request.is_mobile = user_agent.is_mobile # True if the client is a mobile phone
|
|
||||||
request.is_tablet = user_agent.is_tablet # True if the client is a tablet
|
|
||||||
request.is_mobile_device = user_agent.is_mobile or user_agent.is_tablet # True if mobile phone or tablet
|
|
||||||
request.is_touch_capable = user_agent.is_touch_capable # True if the client supports touch input
|
|
||||||
request.is_pc = user_agent.is_pc # True if the client is a PC
|
|
||||||
request.is_bot = user_agent.is_bot # True if the client is a bot
|
|
||||||
|
|
||||||
# Pass the request to the next middleware or view and get the response
|
|
||||||
response = self.get_response(request)
|
|
||||||
|
|
||||||
# Return the processed response
|
|
||||||
return response
|
|
||||||
@ -65,7 +65,6 @@
|
|||||||
{% include 'bundles/timers-js.html' %}
|
{% include 'bundles/timers-js.html' %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(() => {
|
|
||||||
// Data
|
// Data
|
||||||
const timers = [
|
const timers = [
|
||||||
{% for op in optimer %}
|
{% for op in optimer %}
|
||||||
@ -144,6 +143,5 @@
|
|||||||
|
|
||||||
// Start timed updates
|
// Start timed updates
|
||||||
setInterval(timedUpdate, 1000);
|
setInterval(timedUpdate, 1000);
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -88,7 +88,6 @@ MIDDLEWARE = [
|
|||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
"allianceauth.authentication.middleware.UserSettingsMiddleware",
|
"allianceauth.authentication.middleware.UserSettingsMiddleware",
|
||||||
"allianceauth.middleware.DeviceDetectionMiddleware",
|
|
||||||
"django.middleware.locale.LocaleMiddleware",
|
"django.middleware.locale.LocaleMiddleware",
|
||||||
"django.middleware.common.CommonMiddleware",
|
"django.middleware.common.CommonMiddleware",
|
||||||
"django.middleware.csrf.CsrfViewMiddleware",
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
|
|||||||
@ -100,13 +100,13 @@ def jabber_broadcast_view(request):
|
|||||||
if main_char is not None:
|
if main_char is not None:
|
||||||
message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "[" + main_char.corporation_ticker + "]" + \
|
message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "[" + main_char.corporation_ticker + "]" + \
|
||||||
main_char.character_name + " TO: " + \
|
main_char.character_name + " TO: " + \
|
||||||
form.cleaned_data['group'] + " WHEN: " + datetime.datetime.now(datetime.timezone.utc).strftime(
|
form.cleaned_data['group'] + " WHEN: " + datetime.datetime.utcnow().strftime(
|
||||||
"%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n"
|
"%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n"
|
||||||
group_to_send = form.cleaned_data['group']
|
group_to_send = form.cleaned_data['group']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "No character but can send pings?" + " TO: " + \
|
message_to_send = form.cleaned_data['message'] + "\n##### SENT BY: " + "No character but can send pings?" + " TO: " + \
|
||||||
form.cleaned_data['group'] + " WHEN: " + datetime.datetime.now(datetime.timezone.utc).strftime(
|
form.cleaned_data['group'] + " WHEN: " + datetime.datetime.utcnow().strftime(
|
||||||
"%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n"
|
"%Y-%m-%d %H:%M:%S") + " #####\n##### Replies are NOT monitored #####\n"
|
||||||
group_to_send = form.cleaned_data['group']
|
group_to_send = form.cleaned_data['group']
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import random
|
|||||||
import string
|
import string
|
||||||
import calendar
|
import calendar
|
||||||
import re
|
import re
|
||||||
import datetime as dt
|
from datetime import datetime
|
||||||
|
|
||||||
from passlib.apps import phpbb3_context
|
from passlib.apps import phpbb3_context
|
||||||
from django.db import connections
|
from django.db import connections
|
||||||
@ -128,7 +128,7 @@ class Phpbb3Manager:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_current_utc_date():
|
def __get_current_utc_date():
|
||||||
d = dt.datetime.now(dt.timezone.utc)
|
d = datetime.utcnow()
|
||||||
unixtime = calendar.timegm(d.utctimetuple())
|
unixtime = calendar.timegm(d.utctimetuple())
|
||||||
return unixtime
|
return unixtime
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import calendar
|
import calendar
|
||||||
import datetime as dt
|
from datetime import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
@ -105,7 +105,7 @@ class SmfManager:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_current_utc_date():
|
def get_current_utc_date():
|
||||||
d = dt.datetime.now(dt.timezone.utc)
|
d = datetime.utcnow()
|
||||||
unixtime = calendar.timegm(d.utctimetuple())
|
unixtime = calendar.timegm(d.utctimetuple())
|
||||||
return unixtime
|
return unixtime
|
||||||
|
|
||||||
|
|||||||
@ -1,207 +0,0 @@
|
|||||||
/* global fetchGet, numberFormatter, taskQueueSettings */
|
|
||||||
|
|
||||||
$(document).ready(() => {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const elements = {
|
|
||||||
total: document.getElementById('total-task-count'),
|
|
||||||
uptime: document.getElementById('celery-uptime'),
|
|
||||||
running: document.getElementById('running-task-count'),
|
|
||||||
queued: document.getElementById('queued-tasks-count'),
|
|
||||||
succeeded: document.getElementById('succeeded-tasks-count'),
|
|
||||||
retried: document.getElementById('retried-tasks-count'),
|
|
||||||
failed: document.getElementById('failed-tasks-count')
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the task queue status and updates the UI elements accordingly.
|
|
||||||
* It retrieves the total number of tasks, running tasks, queued tasks,
|
|
||||||
* succeeded tasks, retried tasks, and failed tasks, and updates the
|
|
||||||
* corresponding HTML elements with the fetched data.
|
|
||||||
* It also updates the progress bars for succeeded, retried, and failed tasks.
|
|
||||||
* The function is called immediately and then every 30 seconds to keep the
|
|
||||||
* task queue status up to date.
|
|
||||||
*/
|
|
||||||
const updateTaskCount = () => {
|
|
||||||
fetchGet({url: taskQueueSettings.url})
|
|
||||||
.then((data) => {
|
|
||||||
const elemProgressBar = document.getElementById('celery-tasks-progress-bar');
|
|
||||||
const progressElements = {
|
|
||||||
succeeded: {
|
|
||||||
bar: document.getElementById('celery-progress-bar-succeeded'),
|
|
||||||
text: document.getElementById('celery-progress-bar-succeeded-progress')
|
|
||||||
},
|
|
||||||
retried: {
|
|
||||||
bar: document.getElementById('celery-progress-bar-retried'),
|
|
||||||
text: document.getElementById('celery-progress-bar-retried-progress')
|
|
||||||
},
|
|
||||||
failed: {
|
|
||||||
bar: document.getElementById('celery-progress-bar-failed'),
|
|
||||||
text: document.getElementById('celery-progress-bar-failed-progress')
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assign progress data from the fetched data to variables
|
|
||||||
const {
|
|
||||||
earliest_task: earliestTask,
|
|
||||||
tasks_total: tasksTotal,
|
|
||||||
tasks_running: tasksRunning,
|
|
||||||
tasks_queued: tasksQueued,
|
|
||||||
tasks_succeeded: tasksSucceeded,
|
|
||||||
tasks_retried: tasksRetried,
|
|
||||||
tasks_failed: tasksFailed
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the text content of the specified HTML element with the given value.
|
|
||||||
* If the value is null, it sets the text to 'N/A'.
|
|
||||||
* Otherwise, it formats the number using the locale-specific format.
|
|
||||||
*
|
|
||||||
* @param {HTMLElement} element The HTML element to update.
|
|
||||||
* @param {number|null} value The value to set in the element.
|
|
||||||
*/
|
|
||||||
const updateTaskCount = (element, value) => {
|
|
||||||
element.textContent = value === null ? taskQueueSettings.l10n.na : numberFormatter({value: value, locales: taskQueueSettings.l10n.language});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the time since the given timestamp and returns a formatted string.
|
|
||||||
* If the timestamp is null or undefined, it returns 'N/A'.
|
|
||||||
* The returned string is in the format of "X hours, Y minutes" or "X minutes, Y seconds".
|
|
||||||
*
|
|
||||||
* @param {string|null} timestamp The timestamp to calculate the time since.
|
|
||||||
* @returns {string} A formatted string representing the time since the timestamp.
|
|
||||||
*/
|
|
||||||
const timeSince = (timestamp) => {
|
|
||||||
if (!timestamp) {
|
|
||||||
return taskQueueSettings.l10n.na;
|
|
||||||
}
|
|
||||||
|
|
||||||
const diffSecs = Math.floor((Date.now() - new Date(timestamp)) / 1000);
|
|
||||||
|
|
||||||
if (diffSecs >= 3600) {
|
|
||||||
const hours = Math.floor(diffSecs / 3600);
|
|
||||||
const minutes = Math.floor((diffSecs % 3600) / 60);
|
|
||||||
|
|
||||||
if (minutes > 0) {
|
|
||||||
const hourText = hours === 1 ? taskQueueSettings.l10n.hour_singular : taskQueueSettings.l10n.hour_plural;
|
|
||||||
const minuteText = minutes === 1 ? taskQueueSettings.l10n.minute_singular : taskQueueSettings.l10n.minute_plural;
|
|
||||||
|
|
||||||
return `${hours} ${hourText}, ${minutes} ${minuteText}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hourText = hours === 1 ? taskQueueSettings.l10n.hour_singular : taskQueueSettings.l10n.hour_plural;
|
|
||||||
|
|
||||||
return `${hours} ${hourText}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const units = [
|
|
||||||
[
|
|
||||||
60,
|
|
||||||
taskQueueSettings.l10n.minute_singular,
|
|
||||||
taskQueueSettings.l10n.minute_plural
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1,
|
|
||||||
taskQueueSettings.l10n.second_singular,
|
|
||||||
taskQueueSettings.l10n.second_plural
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const [seconds, singular, plural] of units) {
|
|
||||||
const value = Math.floor(diffSecs / seconds);
|
|
||||||
|
|
||||||
if (value > 0) {
|
|
||||||
return `${value} ${value > 1 ? plural : singular}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return `0 ${taskQueueSettings.l10n.second_plural}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the progress bar element and its text content based on the given value and total.
|
|
||||||
* It calculates the percentage of completion and updates the aria attributes and styles accordingly.
|
|
||||||
*
|
|
||||||
* @param {HTMLElement} element The progress bar element to update.
|
|
||||||
* @param {HTMLElement} textElement The text element to update with the percentage.
|
|
||||||
* @param {number} value The current value to set in the progress bar.
|
|
||||||
* @param {number} total The total value for calculating the percentage.
|
|
||||||
*/
|
|
||||||
const updateProgressBar = (element, textElement, value, total) => {
|
|
||||||
const percentage = total ? (value / total) * 100 : 0;
|
|
||||||
|
|
||||||
element.setAttribute('aria-valuenow', percentage.toString());
|
|
||||||
textElement.textContent = `${numberFormatter({value: percentage.toFixed(0), locales: taskQueueSettings.l10n.language})}%`;
|
|
||||||
element.style.width = `${percentage}%`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update task counts
|
|
||||||
[
|
|
||||||
[elements.total, tasksTotal],
|
|
||||||
[elements.running, tasksRunning],
|
|
||||||
[elements.queued, tasksQueued],
|
|
||||||
[elements.succeeded, tasksSucceeded],
|
|
||||||
[elements.retried, tasksRetried],
|
|
||||||
[elements.failed, tasksFailed]
|
|
||||||
].forEach(([element, value]) => {
|
|
||||||
updateTaskCount(element, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update uptime
|
|
||||||
elements.uptime.textContent = timeSince(earliestTask);
|
|
||||||
|
|
||||||
// Update progress bar title
|
|
||||||
const [
|
|
||||||
titleTextSucceeded,
|
|
||||||
titleTextRetried,
|
|
||||||
titleTextFailed
|
|
||||||
] = [
|
|
||||||
[tasksSucceeded, taskQueueSettings.l10n.succeeded],
|
|
||||||
[tasksRetried, taskQueueSettings.l10n.retried],
|
|
||||||
[tasksFailed, taskQueueSettings.l10n.failed]
|
|
||||||
].map(([count, label]) => {
|
|
||||||
return `${numberFormatter({value: count, locales: taskQueueSettings.l10n.language})} ${label}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set the title attribute for the progress bar
|
|
||||||
elemProgressBar.setAttribute(
|
|
||||||
'title',
|
|
||||||
`${titleTextSucceeded}, ${titleTextRetried}, ${titleTextFailed}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update progress bars
|
|
||||||
[
|
|
||||||
tasksSucceeded,
|
|
||||||
tasksRetried,
|
|
||||||
tasksFailed
|
|
||||||
].forEach((count, index) => {
|
|
||||||
const type = ['succeeded', 'retried', 'failed'][index];
|
|
||||||
|
|
||||||
updateProgressBar(
|
|
||||||
progressElements[type].bar,
|
|
||||||
progressElements[type].text,
|
|
||||||
count,
|
|
||||||
tasksTotal
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('Error fetching task queue:', error);
|
|
||||||
|
|
||||||
// If there is an error fetching the task queue, set all elements to 'ERROR'
|
|
||||||
[
|
|
||||||
elements.running,
|
|
||||||
elements.queued,
|
|
||||||
elements.succeeded,
|
|
||||||
elements.retried,
|
|
||||||
elements.failed
|
|
||||||
].forEach((elem) => {
|
|
||||||
elem.textContent = taskQueueSettings.l10n.error;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateTaskCount();
|
|
||||||
setInterval(updateTaskCount, 30000);
|
|
||||||
});
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
$(document).ready(() => {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const activeChildMenuItem = document.querySelector('ul#sidebar-menu ul.collapse a.active');
|
|
||||||
|
|
||||||
if (activeChildMenuItem) {
|
|
||||||
const activeChildMenuUl = activeChildMenuItem.closest('ul');
|
|
||||||
activeChildMenuUl.classList.add('show');
|
|
||||||
|
|
||||||
document.querySelectorAll(`[data-bs-target^="#${activeChildMenuUl.id}"]`)
|
|
||||||
.forEach(element => element.setAttribute('aria-expanded', true));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,32 +1,29 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<div id="esi-alert" class="col-12 collapse">
|
<div id="esi-alert" class="col-12 collapse">
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
<p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p>
|
<p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p>
|
||||||
<hr>
|
<hr>
|
||||||
<pre id="esi-data" class="text-center text-wrap"></pre>
|
<pre id="esi-data" class="text-center text-wrap"></pre>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(() => {
|
const elemCard = document.getElementById('esi-alert');
|
||||||
const elements = {
|
const elemMessage = document.getElementById('esi-data');
|
||||||
card: document.getElementById('esi-alert'),
|
const elemCode = document.getElementById('esi-code');
|
||||||
message: document.getElementById('esi-data'),
|
|
||||||
code: document.getElementById('esi-code')
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchGet({url: '{% url "authentication:esi_check" %}'})
|
fetchGet({url: '{% url "authentication:esi_check" %}'})
|
||||||
.then(({status, data}) => {
|
.then((data) => {
|
||||||
console.log('ESI Check:', JSON.stringify({status, data}, null, 2));
|
console.log('ESI Check: ', JSON.stringify(data, null, 2));
|
||||||
|
|
||||||
if (status !== 200) {
|
if (data.status !== 200) {
|
||||||
elements.code.textContent = status;
|
elemCode.textContent = data.status;
|
||||||
elements.message.textContent = data.error;
|
elemMessage.textContent = data.data.error;
|
||||||
|
|
||||||
new bootstrap.Collapse(elements.card, {toggle: true});
|
new bootstrap.Collapse(elemCard, {toggle: true});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => console.error('Error fetching ESI check:', error));
|
.catch((error) => {
|
||||||
|
console.error('Error fetching ESI check:', error);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</div>
|
|
||||||
|
|||||||
@ -161,24 +161,206 @@ the escapejs filter without having to redefine them later.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const taskQueueSettings = {
|
const elements = {
|
||||||
url: '{% url "authentication:task_counts" %}',
|
total: document.getElementById('total-task-count'),
|
||||||
l10n: {
|
uptime: document.getElementById('celery-uptime'),
|
||||||
language: '{{ LANGUAGE_CODE }}',
|
running: document.getElementById('running-task-count'),
|
||||||
second_singular: '{{ l10nSecondSingular|escapejs }}',
|
queued: document.getElementById('queued-tasks-count'),
|
||||||
second_plural: '{{ l10nSecondPlural|escapejs }}',
|
succeeded: document.getElementById('succeeded-tasks-count'),
|
||||||
minute_singular: '{{ l10nMinuteSingular|escapejs }}',
|
retried: document.getElementById('retried-tasks-count'),
|
||||||
minute_plural: '{{ l10nMinutePlural|escapejs }}',
|
failed: document.getElementById('failed-tasks-count')
|
||||||
hour_singular: '{{ l10nHourSingular|escapejs }}',
|
};
|
||||||
hour_plural: '{{ l10nHourPlural|escapejs }}',
|
|
||||||
na: '{{ l10nNA|escapejs }}',
|
/**
|
||||||
error: '{{ l10nError|escapejs }}',
|
* Fetches the task queue status and updates the UI elements accordingly.
|
||||||
running: '{{ l10nRunning|escapejs }}',
|
* It retrieves the total number of tasks, running tasks, queued tasks,
|
||||||
queued: '{{ l10nQueued|escapejs }}',
|
* succeeded tasks, retried tasks, and failed tasks, and updates the
|
||||||
succeeded: '{{ l10nSucceeded|escapejs }}',
|
* corresponding HTML elements with the fetched data.
|
||||||
retried: '{{ l10nRetried|escapejs }}',
|
* It also updates the progress bars for succeeded, retried, and failed tasks.
|
||||||
failed: '{{ l10nFailed|escapejs }}'
|
* The function is called immediately and then every 30 seconds to keep the
|
||||||
|
* task queue status up to date.
|
||||||
|
*/
|
||||||
|
const updateTaskCount = () => {
|
||||||
|
fetchGet({url: '{% url "authentication:task_counts" %}'})
|
||||||
|
.then((data) => {
|
||||||
|
const numberL10nFormat = new Intl.NumberFormat('{{ LANGUAGE_CODE }}');
|
||||||
|
const elemProgressBar = document.getElementById('celery-tasks-progress-bar');
|
||||||
|
const progressElements = {
|
||||||
|
succeeded: {
|
||||||
|
bar: document.getElementById('celery-progress-bar-succeeded'),
|
||||||
|
text: document.getElementById('celery-progress-bar-succeeded-progress')
|
||||||
|
},
|
||||||
|
retried: {
|
||||||
|
bar: document.getElementById('celery-progress-bar-retried'),
|
||||||
|
text: document.getElementById('celery-progress-bar-retried-progress')
|
||||||
|
},
|
||||||
|
failed: {
|
||||||
|
bar: document.getElementById('celery-progress-bar-failed'),
|
||||||
|
text: document.getElementById('celery-progress-bar-failed-progress')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Assign progress data from the fetched data to variables
|
||||||
|
const {
|
||||||
|
earliest_task: earliestTask,
|
||||||
|
tasks_total: tasksTotal,
|
||||||
|
tasks_running: tasksRunning,
|
||||||
|
tasks_queued: tasksQueued,
|
||||||
|
tasks_succeeded: tasksSucceeded,
|
||||||
|
tasks_retried: tasksRetried,
|
||||||
|
tasks_failed: tasksFailed
|
||||||
|
} = data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the text content of the specified HTML element with the given value.
|
||||||
|
* If the value is null, it sets the text to 'N/A'.
|
||||||
|
* Otherwise, it formats the number using the locale-specific format.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element The HTML element to update.
|
||||||
|
* @param {number|null} value The value to set in the element.
|
||||||
|
*/
|
||||||
|
const updateTaskCount = (element, value) => {
|
||||||
|
element.textContent = value == null ? '{{ l10nNA|escapejs }}' : numberL10nFormat.format(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the time since the given timestamp and returns a formatted string.
|
||||||
|
* If the timestamp is null or undefined, it returns 'N/A'.
|
||||||
|
* The returned string is in the format of "X hours, Y minutes" or "X minutes, Y seconds".
|
||||||
|
*
|
||||||
|
* @param {string|null} timestamp The timestamp to calculate the time since.
|
||||||
|
* @returns {string} A formatted string representing the time since the timestamp.
|
||||||
|
*/
|
||||||
|
const timeSince = (timestamp) => {
|
||||||
|
if (!timestamp) {
|
||||||
|
return '{{ l10nNA|escapejs }}';
|
||||||
|
}
|
||||||
|
|
||||||
|
const diffSecs = Math.floor((Date.now() - new Date(timestamp)) / 1000);
|
||||||
|
|
||||||
|
if (diffSecs >= 3600) {
|
||||||
|
const hours = Math.floor(diffSecs / 3600);
|
||||||
|
const minutes = Math.floor((diffSecs % 3600) / 60);
|
||||||
|
|
||||||
|
if (minutes > 0) {
|
||||||
|
const hourText = hours === 1 ? '{{ l10nHourSingular|escapejs }}' : '{{ l10nHourPlural|escapejs }}';
|
||||||
|
const minuteText = minutes === 1 ? '{{ l10nMinuteSingular|escapejs }}' : '{{ l10nMinutePlural|escapejs }}';
|
||||||
|
|
||||||
|
return `${hours} ${hourText}, ${minutes} ${minuteText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hourText = hours === 1 ? '{{ l10nHourSingular|escapejs }}' : '{{ l10nHourPlural|escapejs }}';
|
||||||
|
|
||||||
|
return `${hours} ${hourText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const units = [
|
||||||
|
[
|
||||||
|
60,
|
||||||
|
'{{ l10nMinuteSingular|escapejs }}',
|
||||||
|
'{{ l10nMinutePlural|escapejs }}'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'{{ l10nSecondSingular|escapejs }}',
|
||||||
|
'{{ l10nSecondPlural|escapejs }}'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const [seconds, singular, plural] of units) {
|
||||||
|
const value = Math.floor(diffSecs / seconds);
|
||||||
|
|
||||||
|
if (value > 0) {
|
||||||
|
return `${value} ${value > 1 ? plural : singular}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '0 {{ l10nSecondPlural|escapejs }}';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the progress bar element and its text content based on the given value and total.
|
||||||
|
* It calculates the percentage of completion and updates the aria attributes and styles accordingly.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element The progress bar element to update.
|
||||||
|
* @param {HTMLElement} textElement The text element to update with the percentage.
|
||||||
|
* @param {number} value The current value to set in the progress bar.
|
||||||
|
* @param {number} total The total value for calculating the percentage.
|
||||||
|
*/
|
||||||
|
const updateProgressBar = (element, textElement, value, total) => {
|
||||||
|
const percentage = total ? (value / total) * 100 : 0;
|
||||||
|
|
||||||
|
element.setAttribute('aria-valuenow', percentage.toString());
|
||||||
|
textElement.textContent = `${numberL10nFormat.format(percentage.toFixed(0))}%`;
|
||||||
|
element.style.width = `${percentage}%`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update task counts
|
||||||
|
[
|
||||||
|
[elements.total, tasksTotal],
|
||||||
|
[elements.running, tasksRunning],
|
||||||
|
[elements.queued, tasksQueued],
|
||||||
|
[elements.succeeded, tasksSucceeded],
|
||||||
|
[elements.retried, tasksRetried],
|
||||||
|
[elements.failed, tasksFailed]
|
||||||
|
].forEach(([element, value]) => {
|
||||||
|
updateTaskCount(element, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update uptime
|
||||||
|
elements.uptime.textContent = timeSince(earliestTask);
|
||||||
|
|
||||||
|
// Update progress bar title
|
||||||
|
const [
|
||||||
|
titleTextSucceeded,
|
||||||
|
titleTextRetried,
|
||||||
|
titleTextFailed
|
||||||
|
] = [
|
||||||
|
[tasksSucceeded, '{{ l10nSucceeded|escapejs }}'],
|
||||||
|
[tasksRetried, '{{ l10nRetried|escapejs }}'],
|
||||||
|
[tasksFailed, '{{ l10nFailed|escapejs }}']
|
||||||
|
].map(([count, label]) => {
|
||||||
|
return `${numberL10nFormat.format(count)} ${label}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the title attribute for the progress bar
|
||||||
|
elemProgressBar.setAttribute(
|
||||||
|
'title',
|
||||||
|
`${titleTextSucceeded}, ${titleTextRetried}, ${titleTextFailed}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update progress bars
|
||||||
|
[
|
||||||
|
tasksSucceeded,
|
||||||
|
tasksRetried,
|
||||||
|
tasksFailed
|
||||||
|
].forEach((count, index) => {
|
||||||
|
const type = ['succeeded', 'retried', 'failed'][index];
|
||||||
|
|
||||||
|
updateProgressBar(
|
||||||
|
progressElements[type].bar,
|
||||||
|
progressElements[type].text,
|
||||||
|
count,
|
||||||
|
tasksTotal
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error fetching task queue:', error);
|
||||||
|
|
||||||
|
// If there is an error fetching the task queue, set all elements to 'ERROR'
|
||||||
|
[
|
||||||
|
elements.running,
|
||||||
|
elements.queued,
|
||||||
|
elements.succeeded,
|
||||||
|
elements.retried,
|
||||||
|
elements.failed
|
||||||
|
].forEach((elem) => {
|
||||||
|
elem.textContent = '{{ l10nError|escapejs }}';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTaskCount();
|
||||||
|
setInterval(updateTaskCount, 30000);
|
||||||
</script>
|
</script>
|
||||||
{% include "bundles/auth-dashboard-task-queue-js.html" %}
|
|
||||||
|
|||||||
@ -102,7 +102,44 @@
|
|||||||
</main>
|
</main>
|
||||||
<!-- End Body -->
|
<!-- End Body -->
|
||||||
|
|
||||||
{% include "bundles/auth-sidebar-collapse-js.html" %}
|
<script>
|
||||||
|
(() => {
|
||||||
|
// TODO Move to own JS file
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
const sidebarKey = `sidebar_${sidebar.id}`;
|
||||||
|
|
||||||
|
sidebar.addEventListener('shown.bs.collapse', (event) => {
|
||||||
|
if (event.target.id === sidebar.id) {
|
||||||
|
localStorage.removeItem(sidebarKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sidebar.addEventListener('hidden.bs.collapse', (event) => {
|
||||||
|
if (event.target.id === sidebar.id) {
|
||||||
|
localStorage.setItem(sidebarKey, 'closed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (localStorage.getItem(sidebarKey) === 'closed') {
|
||||||
|
sidebar.classList.remove('show');
|
||||||
|
} else {
|
||||||
|
sidebar.classList.add('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeChildMenuItem = document.querySelector('#sidebar-menu li ul li a.active');
|
||||||
|
|
||||||
|
if (activeChildMenuItem) {
|
||||||
|
const activeChildMenuUl = activeChildMenuItem.parentElement.parentElement;
|
||||||
|
const elementsToToggle = document.querySelectorAll(`[data-bs-target^="#${activeChildMenuUl.id}"]`);
|
||||||
|
|
||||||
|
activeChildMenuUl.classList.add('show');
|
||||||
|
|
||||||
|
elementsToToggle.forEach((element) => {
|
||||||
|
element.setAttribute('aria-expanded', true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
{% theme_js %}
|
{% theme_js %}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
{% load sri %}
|
|
||||||
|
|
||||||
{% sri_static 'allianceauth/js/dashboard-update-task-queue.js' %}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{% load sri %}
|
|
||||||
|
|
||||||
{% sri_static 'allianceauth/js/sidebar-collapse.js' %}
|
|
||||||
@ -96,7 +96,6 @@
|
|||||||
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
|
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(() => {
|
|
||||||
const timers = [
|
const timers = [
|
||||||
{% for timer in timers %}
|
{% for timer in timers %}
|
||||||
{
|
{
|
||||||
@ -176,6 +175,7 @@
|
|||||||
// Start timed updates
|
// Start timed updates
|
||||||
setInterval(timedUpdate, 1000);
|
setInterval(timedUpdate, 1000);
|
||||||
|
|
||||||
|
$(document).ready(() => {
|
||||||
const dtOptions = {
|
const dtOptions = {
|
||||||
language: {url: '{{ DT_LANG_PATH }}'},
|
language: {url: '{{ DT_LANG_PATH }}'},
|
||||||
order: [
|
order: [
|
||||||
@ -185,10 +185,7 @@
|
|||||||
|
|
||||||
{% if perms.auth.timer_management %}
|
{% if perms.auth.timer_management %}
|
||||||
dtOptions['columnDefs'] = [
|
dtOptions['columnDefs'] = [
|
||||||
{
|
{ "orderable": false, "targets": 7 }
|
||||||
"orderable": false,
|
|
||||||
"targets": 7
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@ -80,10 +80,7 @@ urlpatterns = [
|
|||||||
path('night/', views.NightModeRedirectView.as_view(), name='nightmode'),
|
path('night/', views.NightModeRedirectView.as_view(), name='nightmode'),
|
||||||
|
|
||||||
# Theme Change
|
# Theme Change
|
||||||
path('theme/', views.ThemeRedirectView.as_view(), name='theme'),
|
path('theme/', views.ThemeRedirectView.as_view(), name='theme')
|
||||||
|
|
||||||
# Minimize Menu
|
|
||||||
path('minimize-sidebar/', views.MinimizeSidebarRedirectView.as_view(), name='minimize_sidebar')
|
|
||||||
]
|
]
|
||||||
|
|
||||||
url_hooks = get_hooks("url_hook")
|
url_hooks = get_hooks("url_hook")
|
||||||
|
|||||||
@ -48,29 +48,6 @@ class ThemeRedirectView(View):
|
|||||||
|
|
||||||
return HttpResponseRedirect(request.GET.get("next", "/"))
|
return HttpResponseRedirect(request.GET.get("next", "/"))
|
||||||
|
|
||||||
class MinimizeSidebarRedirectView(View):
|
|
||||||
SESSION_VAR = "MINIMIZE_SIDEBAR"
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
request.session[self.SESSION_VAR] = not self.minimize_sidebar_state(request)
|
|
||||||
if not request.user.is_anonymous:
|
|
||||||
try:
|
|
||||||
request.user.profile.minimize_sidebar = request.session[self.SESSION_VAR]
|
|
||||||
request.user.profile.save()
|
|
||||||
except Exception as e:
|
|
||||||
logger.exception(e)
|
|
||||||
|
|
||||||
return HttpResponseRedirect(request.GET.get("next", "/"))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def minimize_sidebar_state(cls, request):
|
|
||||||
try:
|
|
||||||
return request.session.get(cls.SESSION_VAR, False)
|
|
||||||
except AttributeError:
|
|
||||||
# Session is middleware
|
|
||||||
# Sometimes request wont have a session attribute
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: error views should be renamed to a proper function name when possible
|
# TODO: error views should be renamed to a proper function name when possible
|
||||||
|
|
||||||
|
|||||||
@ -61,14 +61,11 @@ dependencies = [
|
|||||||
"passlib",
|
"passlib",
|
||||||
"pydiscourse",
|
"pydiscourse",
|
||||||
"python-slugify>=1.2",
|
"python-slugify>=1.2",
|
||||||
"pyyaml",
|
|
||||||
"redis>=4",
|
"redis>=4",
|
||||||
"requests>=2.9.1",
|
"requests>=2.9.1",
|
||||||
"requests-oauthlib",
|
"requests-oauthlib",
|
||||||
"semantic-version",
|
"semantic-version",
|
||||||
"slixmpp<1.9",
|
"slixmpp<1.9",
|
||||||
"ua-parser",
|
|
||||||
"user-agents",
|
|
||||||
]
|
]
|
||||||
optional-dependencies.docs = [
|
optional-dependencies.docs = [
|
||||||
"myst-parser",
|
"myst-parser",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user