Compare commits

..
Author SHA1 Message Date
imperosol db44e4bd02 fix user picture delete confirmation page 2026-09-09 22:48:34 +02:00
4 changed files with 88 additions and 99 deletions
+29 -31
View File
@@ -4,15 +4,11 @@
{% trans %}Delete confirmation{% endtrans %} {% trans %}Delete confirmation{% endtrans %}
{% endblock %} {% endblock %}
{% if is_fragment %} {# Don't display tabs and errors #}
{% block tabs %}
{# Don't display tabs and errors #} {% endblock %}
{% block tabs %} {% block errors %}
{% endblock %} {% endblock %}
{% block errors %}
{% endblock %}
{% endif %}
{% block file %} {% block file %}
<h2>{% trans %}Delete confirmation{% endtrans %}</h2> <h2>{% trans %}Delete confirmation{% endtrans %}</h2>
@@ -23,30 +19,32 @@
{% set action = current %} {% set action = current %}
{% endif %} {% endif %}
<form action="{{ action }}" method="post"> <p>{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}</p>
<form
method="post"
{% if is_fragment %}
hx-action="{{ action }}"
hx-target="#content"
hx-swap="innerHTML"
{% else %}
action="{{ action }}"
{% endif %}
>
{% csrf_token %} {% csrf_token %}
<input type="submit" value="{% trans %}Confirm{% endtrans %}" />
<p>{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}</p> </form>
<button <form
{% if is_fragment %} method="get"
hx-post="{{ action }}" {% if is_fragment %}
hx-target="#content" hx-action="{{ previous }}"
hx-swap="innerHTML" hx-target="#content"
{% endif %} hx-swap="innerHTML"
>{% trans %}Confirm{% endtrans %}</button> {% else %}
action="javascript:history.back();"
<button {% endif %}
{% if is_fragment %} >
hx-get="{{ previous }}" <input type="submit" name="cancel" value="{% trans %}Cancel{% endtrans %}" />
hx-target="#content"
hx-swap="innerHTML"
{% else %}
action="window.history.back()"
{% endif %}
>{% trans %}Cancel{% endtrans %}</button>
</form> </form>
{% endblock %} {% endblock %}
+6 -22
View File
@@ -21,7 +21,7 @@
# Place - Suite 330, Boston, MA 02111-1307, USA. # Place - Suite 330, Boston, MA 02111-1307, USA.
# #
# #
from django.urls import path, re_path, register_converter from django.urls import path, register_converter
from django.views.generic import RedirectView from django.views.generic import RedirectView
from com.views import NewsListView from com.views import NewsListView
@@ -193,27 +193,11 @@ urlpatterns = [
name="user_gift_delete", name="user_gift_delete",
), ),
# File views # File views
re_path(r"^file/$", FileListView.as_view(), name="file_list"), path("file/", FileListView.as_view(), name="file_list"),
re_path( path("file/<int:file_id>/", FileView.as_view(), name="file_detail"),
r"^file/(?P<file_id>[0-9]+)/$", path("file/<int:file_id>/edit/", FileEditView.as_view(), name="file_edit"),
FileView.as_view(), path("file/<int:file_id>/prop/", FileEditPropView.as_view(), name="file_prop"),
name="file_detail", path("file/<int:file_id>/delete/", FileDeleteView.as_view(), name="file_delete"),
),
re_path(
r"^file/(?P<file_id>[0-9]+)/edit/$",
FileEditView.as_view(),
name="file_edit",
),
re_path(
r"^file/(?P<file_id>[0-9]+)/prop/$",
FileEditPropView.as_view(),
name="file_prop",
),
re_path(
r"^file/(?P<file_id>[0-9]+)/delete/$",
FileDeleteView.as_view(),
name="file_delete",
),
path("file/moderation/", FileModerationView.as_view(), name="file_moderation"), path("file/moderation/", FileModerationView.as_view(), name="file_moderation"),
path( path(
"file/<int:file_id>/moderate/", FileModerateView.as_view(), name="file_moderate" "file/<int:file_id>/moderate/", FileModerateView.as_view(), name="file_moderate"
+2 -9
View File
@@ -356,15 +356,8 @@ class FileDeleteView(AllowFragment, CanEditPropMixin, DeleteView):
if "next" in self.request.GET: if "next" in self.request.GET:
return self.request.GET["next"] return self.request.GET["next"]
if self.object.parent is None: if self.object.parent is None:
return reverse( return reverse("core:file_list")
"core:file_list", return reverse("core:file_detail", kwargs={"file_id": self.object.parent.id})
)
return reverse(
"core:file_detail",
kwargs={
"file_id": self.object.parent.id,
},
)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs) kwargs = super().get_context_data(**kwargs)
@@ -1,5 +1,9 @@
import { getCurrentUrlParams, updateQueryString } from "#core:utils/history"; import {
import { type SimpleUeSchema, ueFetchUeList } from "#openapi"; getCurrentUrlParams,
History,
updateQueryString,
} from "#core:utils/history.ts";
import { ueFetchUeList } from "#openapi";
const pageDefault = 1; const pageDefault = 1;
const pageSizeDefault = 100; const pageSizeDefault = 100;
@@ -8,42 +12,38 @@ document.addEventListener("alpine:init", () => {
Alpine.data("ue_search", () => ({ Alpine.data("ue_search", () => ({
ues: { ues: {
count: 0, count: 0,
next: null as string | null, next: null,
previous: null as string | null, previous: null,
results: [] as SimpleUeSchema[], results: [],
}, },
loading: false, loading: false,
page: pageDefault, page: pageDefault,
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case
page_size: pageSizeDefault, page_size: pageSizeDefault,
search: "", search: "",
department: [] as string[], department: [],
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case
credit_type: [] as string[], credit_type: [],
semester: [] as string[], semester: [],
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case
to_change: [] as { param: string; value: string }[], to_change: [],
pushstate: History.Push,
// dummy implementation to make TS happy. update: undefined,
// The real function is initialized in init
update: () => {
console.warn("Update not yet initialized");
},
initializeArgs() { initializeArgs() {
const url = getCurrentUrlParams(); const url = getCurrentUrlParams();
this.page = Number.parseInt(url.get("page") || pageDefault.toString(), 10); this.pushstate = History.Replace;
this.page_size = Number.parseInt(
url.get("page_size") || pageSizeDefault.toString(), this.page = Number.parseInt(url.get("page"), 10) || pageDefault;
10, this.page_size = Number.parseInt(url.get("page_size"), 10) || pageSizeDefault;
);
this.search = url.get("search") || ""; this.search = url.get("search") || "";
this.department = url.getAll("department"); this.department = url.getAll("department");
this.credit_type = url.getAll("credit_type"); this.credit_type = url.getAll("credit_type");
/* The semester is easier to use on the backend as an enum (spring/autumn/both/none) /* The semester is easier to use on the backend as an enum (spring/autumn/both/none)
and easier to use on the frontend as an array ([spring, autumn]). and easier to use on the frontend as an array ([spring, autumn]).
Thus there is some conversion involved when both communicate together */ Thus there is some conversion involved when both communicate together */
this.semester = url.get("semester")?.split("_AND_") || []; this.semester = url.has("semester") ? url.get("semester").split("_AND_") : [];
this.update(); this.update();
}, },
@@ -51,11 +51,15 @@ document.addEventListener("alpine:init", () => {
async init() { async init() {
this.update = Alpine.debounce(async () => { this.update = Alpine.debounce(async () => {
/* Create the whole url before changing everything all at once */ /* Create the whole url before changing everything all at once */
for (const val of this.to_change) { const first = this.to_change.shift();
updateQueryString(val.param, val.value); let url = updateQueryString(first.param, first.value, History.None);
for (const value of this.to_change) {
url = updateQueryString(value.param, value.value, History.None, url);
} }
updateQueryString(first.param, first.value, this.pushstate, url);
await this.fetchData(); /* reload data on form change */ await this.fetchData(); /* reload data on form change */
this.to_change = []; this.to_change = [];
this.pushstate = History.Push;
}, 50); }, 50);
const searchParams = ["search", "department", "credit_type", "semester"]; const searchParams = ["search", "department", "credit_type", "semester"];
@@ -63,37 +67,47 @@ document.addEventListener("alpine:init", () => {
for (const param of searchParams) { for (const param of searchParams) {
this.$watch(param, () => { this.$watch(param, () => {
if (this.pushstate !== History.Push) {
/* This means that we are doing a mass param edit */
return;
}
/* Reset pagination on search */ /* Reset pagination on search */
this.page = pageDefault; this.page = pageDefault;
this.page_size = pageSizeDefault; this.page_size = pageSizeDefault;
}); });
} }
for (const param of searchParams.concat(paginationParams)) { for (const param of searchParams.concat(paginationParams)) {
this.$watch(param, (value: string) => { this.$watch(param, (value) => {
this.to_change.push({ param: param, value: value }); this.to_change.push({ param: param, value: value });
this.update(); this.update();
}); });
} }
window.addEventListener("popstate", () => {
this.initializeArgs();
});
this.initializeArgs(); this.initializeArgs();
}, },
async fetchData() { async fetchData() {
this.loading = true; this.loading = true;
const args = {
const res = await ueFetchUeList({ // biome-ignore lint/style/useNamingConvention: api is in snake_case
query: { page_size: this.page_size,
// biome-ignore lint/style/useNamingConvention: api is in snake_case };
page_size: this.page_size, for (const [param, value] of new URL(
// biome-ignore lint/style/useNamingConvention: api is in snake_case window.location.href,
credit_type: this.credit_type.length > 0 ? this.credit_type : undefined, ).searchParams.entries()) {
semester: this.semester.length > 0 ? this.semester : undefined, // Deal with array type params
department: this.department.length > 0 ? this.department : undefined, if (["credit_type", "department", "semester"].includes(param)) {
search: this.search || undefined, if (args[param] === undefined) {
}, args[param] = [];
}); }
if (res.data !== undefined) { args[param].push(value);
this.ues = res.data; } else {
args[param] = value;
}
} }
this.ues = (await ueFetchUeList({ query: args })).data;
this.loading = false; this.loading = false;
}, },