panel-redesign: Help with seventeen topics; every box links to its own
Follows the design commit before it. Written by Sonnet, reviewed by
Opus.
- view/help.go, templates/help.html: the mockup's topics, texts, ids
and menu. The page shows the groups the reader's menu shows:
Overview and Server for the global role, Outbound and Inbound by
reach (Inbound only with the feature on), Account for everyone;
boxes are numbered as shown.
- Box.Help set on the boxes the mockups mark; the inbound MX box has
its link again.
- TestEveryHelpLinkLandsOnATopic also fails on a topic that no page
links to, unless it is listed as menu-only (account) - and on a
stale exception.
Known until the change that follows: the dns, dmarc and account topics
describe the three-choice report address, which the code does not have
yet; Account's DMARC box still links to #dns.
Not verified by running: three sentences rest on Postfix behaviour
(delivery is deferred when a required TLS session cannot be made; a
milter with default_action=tempfail answers 4xx; with "any recipient"
an address the upstream refuses comes back as a bounce).
Evidence
- docs/assets/panel-redesign/evidence/help.png (1440x3600) and
in-domain.png: headless Edge, real templates served locally with
their fixtures - NOT from the container.
- go test -count=1 ./internal/web/... ./cmd/panel: ok (six packages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,13 +6,13 @@ import (
|
||||
"github.com/mixeme/selfpost/internal/web/view"
|
||||
)
|
||||
|
||||
// HandleHelp renders the Help page. It is for both roles; the topics about the
|
||||
// checks of Overview and Health are left out for a viewer who cannot see them.
|
||||
// HandleHelp renders the Help page. It is for both roles; the sections of pages
|
||||
// a viewer cannot open (Overview and Server, Inbound without a reach or with the
|
||||
// feature off, Outbound without a reach) are left out, as is their menu.
|
||||
func (h *Handlers) HandleHelp(w http.ResponseWriter, r *http.Request) {
|
||||
p, ok := h.principal(r)
|
||||
if !ok {
|
||||
if _, ok := h.principal(r); !ok {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
h.view.Render(w, http.StatusOK, "help", view.NewHelp(h.shellMeta(r), p.IsGlobal()))
|
||||
h.view.Render(w, http.StatusOK, "help", view.NewHelp(h.shellMeta(r), h.cfg.InboundEnabled))
|
||||
}
|
||||
|
||||
@@ -347,6 +347,49 @@ func TestHelpPageForBothRoles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Help shows the sections of the pages the reader can open, as the shell does for
|
||||
// the menu: Inbound needs the feature and a reach in it, Outbound a reach in it,
|
||||
// and Overview and Server are the global role's. Account is everyone's.
|
||||
func TestHelpSectionsFollowReachAndFeature(t *testing.T) {
|
||||
h, st := inboundHandlers(t)
|
||||
out, _ := st.AddDomain("out.example.com", "mail")
|
||||
in, _ := st.AddInboundDomain("in.example.com")
|
||||
root := auth.Principal{ID: 1, Username: "admin", Role: auth.RoleGlobal}
|
||||
outOnly := userWith(t, st, "out-only", store.RoleDomain, store.Reach{DomainIDs: []int64{out.ID}})
|
||||
inOnly := userWith(t, st, "in-only", store.RoleDomain, store.Reach{InboundDomainIDs: []int64{in.ID}})
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
who auth.Principal
|
||||
inbound bool
|
||||
want []string
|
||||
lacks []string
|
||||
}{
|
||||
{"global", root, true, []string{"<h2>Overview</h2>", "<h2>Outbound</h2>", "<h2>Inbound</h2>", "<h2>Server</h2>", "<h2>Account</h2>"}, nil},
|
||||
{"global, feature off", root, false, []string{"<h2>Outbound</h2>", "<h2>Server</h2>"}, []string{"<h2>Inbound</h2>", `id="filter"`}},
|
||||
{"outbound only", outOnly, true, []string{"<h2>Outbound</h2>", "<h2>Account</h2>"}, []string{"<h2>Inbound</h2>", "<h2>Overview</h2>", "<h2>Server</h2>"}},
|
||||
{"inbound only", inOnly, true, []string{"<h2>Inbound</h2>", `id="upstream"`, "<h2>Account</h2>"}, []string{"<h2>Outbound</h2>", `id="dns"`, "<h2>Server</h2>"}},
|
||||
{"inbound only, feature off", inOnly, false, []string{"<h2>Account</h2>"}, []string{"<h2>Inbound</h2>", "<h2>Outbound</h2>"}},
|
||||
} {
|
||||
h.cfg.InboundEnabled = tt.inbound
|
||||
rec := send(h.HandleHelp, &tt.who, "GET", "/help", nil, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("%s: GET /help = %d", tt.name, rec.Code)
|
||||
}
|
||||
body := rec.Body.String()
|
||||
for _, w := range tt.want {
|
||||
if !strings.Contains(body, w) {
|
||||
t.Errorf("%s: Help is missing %q", tt.name, w)
|
||||
}
|
||||
}
|
||||
for _, g := range tt.lacks {
|
||||
if strings.Contains(body, g) {
|
||||
t.Errorf("%s: Help carries %q", tt.name, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Backup
|
||||
|
||||
// A refused pre-flight says what to add to docker-compose.yml as plain text: the
|
||||
|
||||
Reference in New Issue
Block a user