Move archive mailbox management to own menu
This commit is contained in:
parent
9a5fb89518
commit
fe3ece17ba
2 changed files with 87 additions and 16 deletions
|
|
@ -22,6 +22,7 @@ func RegisterRoutes(mux *http.ServeMux) {
|
||||||
mux.HandleFunc("/accounts/save", accountSaveHandler) // TODO Codex
|
mux.HandleFunc("/accounts/save", accountSaveHandler) // TODO Codex
|
||||||
mux.HandleFunc("/accounts/delete", accountDeleteHandler) // TODO Codex
|
mux.HandleFunc("/accounts/delete", accountDeleteHandler) // TODO Codex
|
||||||
mux.HandleFunc("/accounts/test", accountTestHandler) // TODO Codex: Quelle+Ziel-Login pruefen
|
mux.HandleFunc("/accounts/test", accountTestHandler) // TODO Codex: Quelle+Ziel-Login pruefen
|
||||||
|
mux.HandleFunc("/archives", archivesHandler)
|
||||||
mux.HandleFunc("/archives/create", archiveCreateHandler)
|
mux.HandleFunc("/archives/create", archiveCreateHandler)
|
||||||
mux.HandleFunc("/archives/delete", archiveDeleteHandler)
|
mux.HandleFunc("/archives/delete", archiveDeleteHandler)
|
||||||
mux.HandleFunc("/migrate/run", migrateRunHandler) // TODO Codex: RunMigration im Hintergrund
|
mux.HandleFunc("/migrate/run", migrateRunHandler) // TODO Codex: RunMigration im Hintergrund
|
||||||
|
|
@ -54,9 +55,9 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Mail-Graveyard</title>
|
<title>Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-7">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-8">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-7" defer></script></head>
|
<script src="/static/app.js?v=20260712-8" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -73,6 +74,7 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
|
||||||
<a class="backstage-item active" href="/view">Archiv-Boxen</a>
|
<a class="backstage-item active" href="/view">Archiv-Boxen</a>
|
||||||
<a class="backstage-item" href="/view">Import/Export</a>
|
<a class="backstage-item" href="/view">Import/Export</a>
|
||||||
<a class="backstage-item" href="/accounts">Konten-Verwaltung</a>
|
<a class="backstage-item" href="/accounts">Konten-Verwaltung</a>
|
||||||
|
<a class="backstage-item sub" href="/archives">Archiv-mbox Verwaltung</a>
|
||||||
<a class="backstage-item" href="/view">Mail-Transfer</a>
|
<a class="backstage-item" href="/view">Mail-Transfer</a>
|
||||||
<a class="backstage-item" href="/accounts">Einstellungen</a>
|
<a class="backstage-item" href="/accounts">Einstellungen</a>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
@ -177,6 +179,15 @@ func accountTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
redirectAccounts(w, r, name, "Quelle und Ziel erfolgreich getestet.")
|
redirectAccounts(w, r, name, "Quelle und Ziel erfolgreich getestet.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func archivesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
accounts, err := ListAccounts()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
renderArchivesPage(w, archiveMailboxes(accounts), accounts, r.URL.Query().Get("msg"), r.URL.Query().Get("err"))
|
||||||
|
}
|
||||||
|
|
||||||
func archiveCreateHandler(w http.ResponseWriter, r *http.Request) {
|
func archiveCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
|
@ -184,14 +195,14 @@ func archiveCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
name, err := safeArchiveName(r.FormValue("archive_name"))
|
name, err := safeArchiveName(r.FormValue("archive_name"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
redirectAccounts(w, r, "", err.Error())
|
redirectArchives(w, r, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(filepath.Join(Cfg.MboxRoot, name), 0o700); err != nil {
|
if err := os.MkdirAll(filepath.Join(Cfg.MboxRoot, name), 0o700); err != nil {
|
||||||
redirectAccounts(w, r, "", err.Error())
|
redirectArchives(w, r, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
redirectAccounts(w, r, "", "Archiv-Mailbox angelegt.")
|
redirectArchives(w, r, "Archiv-Mailbox angelegt.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func archiveDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
func archiveDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -201,32 +212,32 @@ func archiveDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
name, err := safeArchiveName(r.FormValue("archive_name"))
|
name, err := safeArchiveName(r.FormValue("archive_name"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
redirectAccounts(w, r, "", err.Error())
|
redirectArchives(w, r, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if archiveInUse(name) {
|
if archiveInUse(name) {
|
||||||
redirectAccounts(w, r, "", "Archiv-Mailbox ist noch einem Postfach zugeordnet.")
|
redirectArchives(w, r, "Archiv-Mailbox ist noch einem Postfach zugeordnet.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
path := filepath.Join(Cfg.MboxRoot, name)
|
path := filepath.Join(Cfg.MboxRoot, name)
|
||||||
entries, err := os.ReadDir(path)
|
entries, err := os.ReadDir(path)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
redirectAccounts(w, r, "", "Archiv-Mailbox existiert nicht.")
|
redirectArchives(w, r, "Archiv-Mailbox existiert nicht.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
redirectAccounts(w, r, "", err.Error())
|
redirectArchives(w, r, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(entries) > 0 {
|
if len(entries) > 0 {
|
||||||
redirectAccounts(w, r, "", "Archiv-Mailbox ist nicht leer und wurde nicht entfernt.")
|
redirectArchives(w, r, "Archiv-Mailbox ist nicht leer und wurde nicht entfernt.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := os.Remove(path); err != nil {
|
if err := os.Remove(path); err != nil {
|
||||||
redirectAccounts(w, r, "", err.Error())
|
redirectArchives(w, r, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
redirectAccounts(w, r, "", "Leere Archiv-Mailbox entfernt.")
|
redirectArchives(w, r, "Leere Archiv-Mailbox entfernt.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateRunHandler(w http.ResponseWriter, r *http.Request) {
|
func migrateRunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -284,9 +295,9 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Konten-Verwaltung - Mail-Graveyard</title>
|
<title>Konten-Verwaltung - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-7">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-8">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-7" defer></script></head>
|
<script src="/static/app.js?v=20260712-8" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -303,6 +314,7 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
|
||||||
<a class="backstage-item" href="/view">Archiv-Boxen</a>
|
<a class="backstage-item" href="/view">Archiv-Boxen</a>
|
||||||
<a class="backstage-item" href="/view">Import/Export</a>
|
<a class="backstage-item" href="/view">Import/Export</a>
|
||||||
<a class="backstage-item active" href="/accounts">Konten-Verwaltung</a>
|
<a class="backstage-item active" href="/accounts">Konten-Verwaltung</a>
|
||||||
|
<a class="backstage-item sub" href="/archives">Archiv-mbox Verwaltung</a>
|
||||||
<a class="backstage-item" href="/view">Mail-Transfer</a>
|
<a class="backstage-item" href="/view">Mail-Transfer</a>
|
||||||
<a class="backstage-item" href="/accounts">Einstellungen</a>
|
<a class="backstage-item" href="/accounts">Einstellungen</a>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
@ -316,13 +328,52 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, `<section class="account-layout"><div class="account-list"><div class="pane-head">Eingetragene Postfaecher</div>`)
|
fmt.Fprint(w, `<section class="account-layout"><div class="account-list"><div class="pane-head">Eingetragene Postfaecher</div>`)
|
||||||
renderAccountsTable(w, accounts, edit.Name)
|
renderAccountsTable(w, accounts, edit.Name)
|
||||||
fmt.Fprint(w, `<div class="pane-head">Archiv-mbox verwalten</div>`)
|
|
||||||
renderArchiveManager(w, archives, accounts)
|
|
||||||
fmt.Fprint(w, `</div><div class="account-form-panel"><div class="pane-head">Postfach eintragen</div>`)
|
fmt.Fprint(w, `</div><div class="account-form-panel"><div class="pane-head">Postfach eintragen</div>`)
|
||||||
renderAccountForm(w, edit, archives)
|
renderAccountForm(w, edit, archives)
|
||||||
fmt.Fprint(w, `</div></section></main><footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`)
|
fmt.Fprint(w, `</div></section></main><footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func renderArchivesPage(w http.ResponseWriter, archives []string, accounts []Account, msg, errMsg string) {
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Archiv-mbox Verwaltung - Mail-Graveyard</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css?v=20260712-8">
|
||||||
|
<script src="/static/htmx.min.js"></script>
|
||||||
|
<script src="/static/app.js?v=20260712-8" defer></script></head>
|
||||||
|
<body class="ol2013">
|
||||||
|
<header class="ribbon">
|
||||||
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
<nav class="ribbon-tabs">
|
||||||
|
<a class="tab" href="/view">Archiv-Boxen</a>
|
||||||
|
<a class="tab" href="/view">Import/Export</a>
|
||||||
|
<a class="tab" href="/accounts">Konten-Verwaltung</a>
|
||||||
|
<a class="tab" href="/view">Mail-Transfer</a>
|
||||||
|
<a class="tab" href="/accounts">Einstellungen</a>
|
||||||
|
</nav>
|
||||||
|
<div class="ribbon-title">Mail-Graveyard</div>
|
||||||
|
</header>
|
||||||
|
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
|
||||||
|
<a class="backstage-item" href="/view">Archiv-Boxen</a>
|
||||||
|
<a class="backstage-item" href="/view">Import/Export</a>
|
||||||
|
<a class="backstage-item" href="/accounts">Konten-Verwaltung</a>
|
||||||
|
<a class="backstage-item sub active" href="/archives">Archiv-mbox Verwaltung</a>
|
||||||
|
<a class="backstage-item" href="/view">Mail-Transfer</a>
|
||||||
|
<a class="backstage-item" href="/accounts">Einstellungen</a>
|
||||||
|
</aside>
|
||||||
|
<button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button>
|
||||||
|
<main class="accounts-page archive-page">`)
|
||||||
|
if msg != "" {
|
||||||
|
fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg))
|
||||||
|
}
|
||||||
|
if errMsg != "" {
|
||||||
|
fmt.Fprintf(w, `<div class="notice bad">%s</div>`, html.EscapeString(errMsg))
|
||||||
|
}
|
||||||
|
fmt.Fprint(w, `<section class="archive-page-layout"><div class="pane-head">Archiv-mbox Verwaltung</div>`)
|
||||||
|
renderArchiveManager(w, archives, accounts)
|
||||||
|
fmt.Fprint(w, `</section></main><footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`)
|
||||||
|
}
|
||||||
|
|
||||||
func renderAccountsTable(w http.ResponseWriter, accounts []Account, editName string) {
|
func renderAccountsTable(w http.ResponseWriter, accounts []Account, editName string) {
|
||||||
if len(accounts) == 0 {
|
if len(accounts) == 0 {
|
||||||
fmt.Fprint(w, `<div class="ef-empty">Noch keine Postfaecher eingetragen.</div>`)
|
fmt.Fprint(w, `<div class="ef-empty">Noch keine Postfaecher eingetragen.</div>`)
|
||||||
|
|
@ -581,6 +632,14 @@ func redirectAccounts(w http.ResponseWriter, r *http.Request, edit, msg string)
|
||||||
http.Redirect(w, r, target, http.StatusSeeOther)
|
http.Redirect(w, r, target, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func redirectArchives(w http.ResponseWriter, r *http.Request, msg string) {
|
||||||
|
key := "msg"
|
||||||
|
if strings.Contains(strings.ToLower(msg), "fehl") || strings.Contains(strings.ToLower(msg), "ungueltig") || strings.Contains(strings.ToLower(msg), "nicht") || strings.Contains(strings.ToLower(msg), "error") {
|
||||||
|
key = "err"
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/archives?"+key+"="+urlQuery(msg), http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
func urlQuery(value string) string {
|
func urlQuery(value string) string {
|
||||||
return url.QueryEscape(value)
|
return url.QueryEscape(value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
frontend-js/dist/style.css
vendored
12
frontend-js/dist/style.css
vendored
|
|
@ -120,6 +120,10 @@ body.ol2013 {
|
||||||
color: var(--ol-blue);
|
color: var(--ol-blue);
|
||||||
border-left-color: #fff;
|
border-left-color: #fff;
|
||||||
}
|
}
|
||||||
|
.backstage-item.sub {
|
||||||
|
padding-left: 38px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
.backstage-shade {
|
.backstage-shade {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 44px 0 22px 0;
|
inset: 44px 0 22px 0;
|
||||||
|
|
@ -348,6 +352,14 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
.account-form-panel { border-right: 0; }
|
.account-form-panel { border-right: 0; }
|
||||||
|
.archive-page {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.archive-page-layout {
|
||||||
|
max-width: 900px;
|
||||||
|
min-height: 100%;
|
||||||
|
border-right: 1px solid var(--ol-border);
|
||||||
|
}
|
||||||
.account-table {
|
.account-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue