mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 10:35:30 +00:00
+person delete endpond, BUGFIX: some visual inconistencies
This commit is contained in:
@@ -330,7 +330,7 @@ func (p *AlmanachEditPage) POSTDelete(engine *templating.Engine, app core.App) H
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{
|
||||
"success": true,
|
||||
"redirect": "/suche/baende",
|
||||
"redirect": "/reihen",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ func (p *PersonEditPage) Setup(router *router.Router[*core.RequestEvent], app co
|
||||
rg.BindFunc(middleware.IsAdminOrEditor())
|
||||
rg.GET(URL_PERSON_EDIT, p.GET(engine, app))
|
||||
rg.POST(URL_PERSON_EDIT, p.POST(engine, app))
|
||||
rg.POST(URL_PERSON_EDIT+"/delete", p.POSTDelete(engine, app))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -247,6 +248,11 @@ type personEditForm struct {
|
||||
Comment string `form:"edit_comment"`
|
||||
}
|
||||
|
||||
type personDeletePayload struct {
|
||||
CSRFToken string `json:"csrf_token"`
|
||||
LastEdited string `json:"last_edited"`
|
||||
}
|
||||
|
||||
func applyPersonForm(agent *dbmodels.Agent, formdata personEditForm, name string, status string, user *dbmodels.FixedUser) {
|
||||
agent.SetName(name)
|
||||
agent.SetPseudonyms(strings.TrimSpace(formdata.Pseudonyms))
|
||||
@@ -341,3 +347,108 @@ func (p *PersonEditPage) POST(engine *templating.Engine, app core.App) HandleFun
|
||||
return e.Redirect(http.StatusSeeOther, redirect)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PersonEditPage) POSTDelete(engine *templating.Engine, app core.App) HandleFunc {
|
||||
return func(e *core.RequestEvent) error {
|
||||
id := e.Request.PathValue("id")
|
||||
req := templating.NewRequest(e)
|
||||
|
||||
payload := personDeletePayload{}
|
||||
if err := e.BindBody(&payload); err != nil {
|
||||
return e.JSON(http.StatusBadRequest, map[string]any{
|
||||
"error": "Ungültige Formulardaten.",
|
||||
})
|
||||
}
|
||||
|
||||
if err := req.CheckCSRF(payload.CSRFToken); err != nil {
|
||||
return e.JSON(http.StatusBadRequest, map[string]any{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
agent, err := dbmodels.Agents_ID(app, id)
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusNotFound, map[string]any{
|
||||
"error": "Person wurde nicht gefunden.",
|
||||
})
|
||||
}
|
||||
|
||||
if payload.LastEdited != "" {
|
||||
lastEdited, err := types.ParseDateTime(payload.LastEdited)
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusBadRequest, map[string]any{
|
||||
"error": "Ungültiger Bearbeitungszeitstempel.",
|
||||
})
|
||||
}
|
||||
if !agent.Updated().Time().Equal(lastEdited.Time()) {
|
||||
return e.JSON(http.StatusConflict, map[string]any{
|
||||
"error": "Die Person wurde inzwischen geändert. Bitte Seite neu laden.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.RunInTransaction(func(tx core.App) error {
|
||||
if err := deleteAgentRelations(tx, agent.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
record, err := tx.FindRecordById(dbmodels.AGENTS_TABLE, agent.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Delete(record)
|
||||
}); err != nil {
|
||||
app.Logger().Error("Failed to delete agent", "agent_id", agent.Id, "error", err)
|
||||
return e.JSON(http.StatusInternalServerError, map[string]any{
|
||||
"error": "Löschen fehlgeschlagen.",
|
||||
})
|
||||
}
|
||||
|
||||
// Delete from FTS5 index asynchronously
|
||||
go func(appInstance core.App, agentID string) {
|
||||
if err := dbmodels.DeleteFTS5Agent(appInstance, agentID); err != nil {
|
||||
appInstance.Logger().Error("Failed to delete FTS5 agent", "agent_id", agentID, "error", err)
|
||||
}
|
||||
}(app, agent.Id)
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{
|
||||
"success": true,
|
||||
"redirect": "/personen/",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func deleteAgentRelations(tx core.App, agentID string) error {
|
||||
// Delete all REntriesAgents relations
|
||||
entryRelations, err := dbmodels.REntriesAgents_Agent(tx, agentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entryTable := dbmodels.RelationTableName(dbmodels.ENTRIES_TABLE, dbmodels.AGENTS_TABLE)
|
||||
for _, relation := range entryRelations {
|
||||
record, err := tx.FindRecordById(entryTable, relation.Id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := tx.Delete(record); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all RContentsAgents relations
|
||||
contentRelations, err := dbmodels.RContentsAgents_Agent(tx, agentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contentTable := dbmodels.RelationTableName(dbmodels.CONTENTS_TABLE, dbmodels.AGENTS_TABLE)
|
||||
for _, relation := range contentRelations {
|
||||
record, err := tx.FindRecordById(contentTable, relation.Id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := tx.Delete(record); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
TODO danach:
|
||||
- Input:
|
||||
- MO Input:
|
||||
- Titelauflage von/ hat TA
|
||||
- Zeilenumbrüche in Reihen-Annotationen (EVTL. fix in TinyMCE)
|
||||
- Status: Auopsiert, Erfasst etc.
|
||||
- Status farbig
|
||||
- Lösch-Links in Liste, Übersicht u.s.w. (? CSRF-Token fehlt)
|
||||
- Löschen von Personen: werden relationen zu Inhalten mitgelöscht? optional inhalte löschen?
|
||||
- Display von Status u. Bearbeitungsvermerk in Almanach-Ansicht für eingeloggte Nutzer
|
||||
- ACHTUNG! FTS5-Tabellen müssen beim Speichern (und löschen) synchronisiert werden!
|
||||
- Hilfe-Texte für Felder
|
||||
- SO Status farbig
|
||||
- SO Löschen von Personen: werden relationen zu Inhalten mitgelöscht? optional inhalte löschen?
|
||||
- SO Display von Status u. Bearbeitungsvermerk in Almanach-Ansicht für eingeloggte Nutzer
|
||||
- SO Hilfe-Texte für Felder
|
||||
- MO Lösch-Links in Liste, Übersicht u.s.w. (? CSRF-Token fehlt)
|
||||
|
||||
|
||||
Features:
|
||||
- SO, DI Double detection für Ortsnamen, Personennamen, Reihentitel, Kurztitel
|
||||
- NÄCHSTE WOCHE Datenbank-Hygiene
|
||||
- DI Extra-DB für FTS5: ist eigentlich nichtTeil der Haupt-DB, sondern nur Suchindex
|
||||
- Suchindex beim Start erstellen, anstatt dauerhaft zu speichern
|
||||
|
||||
BUGS:
|
||||
- DI: Schriftgröße edit-Screen
|
||||
- MO: doppelte Einträge Reihen-Liste
|
||||
- S. Abendstunden
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -34,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div class="container-normal mt-12 flex flex-col font-serif">
|
||||
<div class="font-sans"><i class="ri-article-line"></i> Einzelbeitrag</div>
|
||||
<div class="font-sans mb-1"><i class="ri-article-line"></i> Einzelbeitrag</div>
|
||||
<h1 class="text-3xl font-bold">
|
||||
{{ $model.result.Entry.PreferredTitle }},
|
||||
{{ if $model.result.Content.Extent -}}
|
||||
@@ -45,13 +45,13 @@
|
||||
{{ $model.result.Content.MusenalmID }}
|
||||
{{- end -}}
|
||||
</h1>
|
||||
<span class="">
|
||||
<span class="mt-1">
|
||||
{{- $arr := $model.result.Content.MusenalmType -}}
|
||||
{{- if $arr -}}
|
||||
{{- range $i, $p := $arr -}}
|
||||
<span
|
||||
class="inline-block align-middle bg-slate-200 px-2 font-sans text-sm py-0.5
|
||||
rounded mx-1 mt-1.5 hover:text-slate-900 no-underline">
|
||||
rounded not-first:ml-1 mr-1 mt-1.5 hover:text-slate-900 no-underline">
|
||||
{{- $p -}}
|
||||
</span>
|
||||
{{- end -}}
|
||||
|
||||
@@ -173,42 +173,43 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{- if not $model.is_new -}}
|
||||
<dialog data-role="edit-delete-dialog" class="fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
||||
<div class="p-5 w-[26rem]">
|
||||
<div class="text-base font-bold text-gray-900">Ort löschen?</div>
|
||||
<div class="text-sm font-bold text-gray-900 mt-1">{{ $place.Name }}</div>
|
||||
<p class="text-sm text-gray-700 mt-2">
|
||||
Der Ort wird gelöscht und aus allen verknüpften Bänden entfernt.
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<div class="text-sm font-semibold text-gray-700">Betroffene Bände</div>
|
||||
<div class="mt-2 max-h-40 overflow-auto pr-1">
|
||||
{{- if $model.result.Entries -}}
|
||||
<ul class="flex flex-col gap-2 pl-0 pr-0 m-0 list-none">
|
||||
{{- range $entry := $model.result.Entries -}}
|
||||
<li class="flex items-baseline justify-between gap-3 ml-0 pl-0 text-sm text-gray-700">
|
||||
<span>{{ $entry.PreferredTitle }}</span>
|
||||
<span class="text-xs text-gray-500">{{ $entry.Year }}</span>
|
||||
</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
{{- else -}}
|
||||
<div class="italic text-gray-500">Keine Bände betroffen.</div>
|
||||
{{- end -}}
|
||||
{{- if not $model.is_new -}}
|
||||
<dialog data-role="edit-delete-dialog" class="fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
||||
<div class="p-5 w-[22rem]">
|
||||
<div class="text-base font-bold text-gray-900">Ort löschen?</div>
|
||||
<div class="text-sm font-bold text-gray-900 mt-1">{{ $place.Name }}</div>
|
||||
<p class="text-sm text-gray-700 mt-2">
|
||||
Der Ort wird gelöscht und aus allen verknüpften Bänden entfernt.
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<div class="text-sm font-semibold text-gray-700">Betroffene Bände</div>
|
||||
<div class="mt-2 max-h-40 overflow-auto pr-1">
|
||||
{{- if $model.result.Entries -}}
|
||||
<ul class="flex flex-col gap-2 pl-0 pr-0 m-0 list-none">
|
||||
{{- range $entry := $model.result.Entries -}}
|
||||
<li class="flex items-baseline justify-between gap-3 ml-0 pl-0 text-sm text-gray-700">
|
||||
<span>{{ $entry.PreferredTitle }}</span>
|
||||
<span class="text-xs text-gray-500">{{ $entry.Year }}</span>
|
||||
</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
{{- else -}}
|
||||
<div class="italic text-gray-500">Keine Bände betroffen.</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-3 mt-4">
|
||||
<button type="button" class="resetbutton w-auto px-3 py-1 text-sm" data-role="edit-delete-cancel">Abbrechen</button>
|
||||
<button
|
||||
type="button"
|
||||
class="submitbutton w-auto bg-red-700 hover:bg-red-800 px-3 py-1 text-sm"
|
||||
data-role="edit-delete-confirm">
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-3 mt-4">
|
||||
<button type="button" class="resetbutton w-auto px-3 py-1 text-sm" data-role="edit-delete-cancel">Abbrechen</button>
|
||||
<button type="button" class="submitbutton w-auto bg-red-700 hover:bg-red-800 px-3 py-1
|
||||
text-sm text-slate-50" data-role="edit-delete-confirm">
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{{- end -}}
|
||||
</dialog>
|
||||
{{- end -}}
|
||||
</form>
|
||||
</div>
|
||||
</edit-page>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
|
||||
<div class="container-normal font-serif mt-12">
|
||||
<div class="flex inline-flex flex-inline gap-x-3">
|
||||
<div class="flex inline-flex flex-inline gap-x-3 mb-1">
|
||||
{{ if $model.result.Agent.CorporateBody }}
|
||||
<div class="font-sans">
|
||||
<i class="ri-team-line"></i>
|
||||
|
||||
@@ -104,7 +104,10 @@
|
||||
class="w-full dbform"
|
||||
id="changepersonform"
|
||||
method="POST"
|
||||
action="{{ if $model.is_new }}/personen/new/{{ else }}/person/{{ $agent.Id }}/edit{{ end }}">
|
||||
action="{{ if $model.is_new }}/personen/new/{{ else }}/person/{{ $agent.Id }}/edit{{ end }}"
|
||||
{{- if not $model.is_new -}}
|
||||
data-delete-endpoint="/person/{{ $agent.Id }}/edit/delete"
|
||||
{{- end -}}>
|
||||
<input type="hidden" name="csrf_token" value="{{ $model.csrf_token }}" />
|
||||
<input type="hidden" name="last_edited" value="{{ if not $model.is_new }}{{ $agent.Updated }}{{ end }}" />
|
||||
|
||||
@@ -186,6 +189,13 @@
|
||||
<i class="ri-loop-left-line"></i>
|
||||
<span>Reset</span>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
||||
data-role="edit-delete">
|
||||
<i class="ri-delete-bin-line"></i>
|
||||
<span>Person löschen</span>
|
||||
</button>
|
||||
{{- end -}}
|
||||
<button type="submit" class="submitbutton w-40 flex items-center gap-2 justify-center">
|
||||
<i class="ri-save-line"></i>
|
||||
@@ -193,6 +203,26 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{- if not $model.is_new -}}
|
||||
<dialog data-role="edit-delete-dialog" class="fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
||||
<div class="p-5 w-[22rem]">
|
||||
<div class="text-base font-bold text-gray-900">Person löschen?</div>
|
||||
<div class="text-sm font-bold text-gray-900 mt-1">{{ $agent.Name }}</div>
|
||||
<p class="text-sm text-gray-700 mt-2">
|
||||
Die Person wird dauerhaft gelöscht. Verknüpfungen zu Bänden und Beiträgen werden entfernt.
|
||||
</p>
|
||||
<div class="flex items-center justify-end gap-3 mt-4">
|
||||
<button type="button" class="resetbutton w-auto px-3 py-1 text-sm" data-role="edit-delete-cancel">Abbrechen</button>
|
||||
<button
|
||||
type="button"
|
||||
class="submitbutton w-auto bg-red-700 hover:bg-red-800 px-3 py-1 text-sm"
|
||||
data-role="edit-delete-confirm">
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{{- end -}}
|
||||
</form>
|
||||
</div>
|
||||
</edit-page>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
|
||||
<div class="container-normal flex flex-col font-serif mt-12">
|
||||
<div class="flex inline-flex flex-inline gap-x-3">
|
||||
<div class="flex inline-flex flex-inline gap-x-3 mb-1">
|
||||
<div class="font-sans">
|
||||
{{/* <svg
|
||||
class="w-[0.9rem] h-[0.9rem] relative bottom-[0.04rem] inline-block"
|
||||
|
||||
@@ -174,41 +174,43 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{- if not $model.is_new -}}
|
||||
<dialog data-role="edit-delete-dialog" class="fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
||||
<div class="p-5 w-[26rem]">
|
||||
<div class="text-base font-bold text-gray-900">Reihe löschen?</div>
|
||||
<div class="text-sm font-bold text-gray-900 mt-1">{{ $series.Title }}</div>
|
||||
<p class="text-sm text-gray-700 mt-2">
|
||||
Alle Bände, Inhalte und Verknüpfungen der bevorzugten Reihentitel werden gelöscht.
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<div class="text-sm font-semibold text-gray-700">Betroffene Bände</div>
|
||||
<div class="mt-2 max-h-40 overflow-auto pr-1">
|
||||
{{- if $model.result.PreferredEntries -}}
|
||||
<ul class="flex flex-col gap-2 pl-0 pr-0 m-0 list-none">
|
||||
{{- range $entry := $model.result.PreferredEntries -}}
|
||||
<li class="flex items-baseline justify-between gap-3 ml-0 pl-0 text-sm text-gray-700">
|
||||
<span>{{ $entry.PreferredTitle }}</span>
|
||||
<span class="text-xs text-gray-500">{{ $entry.Year }}</span>
|
||||
</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
{{- else -}}
|
||||
<div class="italic text-gray-500">Keine Bände betroffen.</div>
|
||||
{{- end -}}
|
||||
{{- if not $model.is_new -}}
|
||||
<dialog data-role="edit-delete-dialog" class="fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
||||
<div class="p-5 w-[22rem]">
|
||||
<div class="text-base font-bold text-gray-900">Reihe löschen?</div>
|
||||
<div class="text-sm font-bold text-gray-900 mt-1">{{ $series.Title }}</div>
|
||||
<p class="text-sm text-gray-700 mt-2">
|
||||
Alle Bände, Inhalte und Verknüpfungen der bevorzugten Reihentitel werden gelöscht.
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<div class="text-sm font-semibold text-gray-700">Betroffene Bände</div>
|
||||
<div class="mt-2 max-h-40 overflow-auto pr-1">
|
||||
{{- if $model.result.PreferredEntries -}}
|
||||
<ul class="flex flex-col gap-2 pl-0 pr-0 m-0 list-none">
|
||||
{{- range $entry := $model.result.PreferredEntries -}}
|
||||
<li class="flex items-baseline justify-between gap-3 ml-0 pl-0 text-sm text-gray-700">
|
||||
<span>{{ $entry.PreferredTitle }}</span>
|
||||
<span class="text-xs text-gray-500">{{ $entry.Year }}</span>
|
||||
</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
{{- else -}}
|
||||
<div class="italic text-gray-500">Keine Bände betroffen.</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-3 mt-4">
|
||||
<button type="button" class="resetbutton w-auto px-3 py-1 text-sm" data-role="edit-delete-cancel">Abbrechen</button>
|
||||
<button
|
||||
type="button"
|
||||
class="submitbutton w-auto bg-red-700 hover:bg-red-800 px-3 py-1 text-sm"
|
||||
data-role="edit-delete-confirm">
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-3 mt-4">
|
||||
<button type="button" class="resetbutton w-auto px-3 py-1 text-sm" data-role="edit-delete-cancel">Abbrechen</button>
|
||||
<button type="button" class="submitbutton w-auto bg-red-700 hover:bg-red-800 px-3
|
||||
py-1 text-sm text-white" data-role="edit-delete-confirm">
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{{- end -}}
|
||||
</dialog>
|
||||
{{- end -}}
|
||||
</form>
|
||||
</div>
|
||||
</edit-page>
|
||||
|
||||
Reference in New Issue
Block a user