writeConfirmPage renders the token-gated preview + POST form. This is where the body preview lives — the reviewer has already opened the link in their browser, so showing held content here is acceptable. The form targets the same URL path with method POST so the actual state change only fires on an
(w http.ResponseWriter, status int, action, token string, msg *identity.Message)
| 680 | // The form targets the same URL path with method POST so the actual |
| 681 | // state change only fires on an explicit click. |
| 682 | func writeConfirmPage(w http.ResponseWriter, status int, action, token string, msg *identity.Message) { |
| 683 | setMagicHeaders(w, status) |
| 684 | |
| 685 | var actionPath, submitLabel, eyebrow, title, lede, submitClass string |
| 686 | switch action { |
| 687 | case approvaltoken.ActionApprove: |
| 688 | actionPath = "/v1/approve" |
| 689 | submitLabel = "Approve & send" |
| 690 | submitClass = "btn btn-primary" |
| 691 | eyebrow = "Pending · approve" |
| 692 | title = "Approve message" |
| 693 | lede = "This message will be sent from your agent immediately." |
| 694 | case approvaltoken.ActionReject: |
| 695 | actionPath = "/v1/reject" |
| 696 | submitLabel = "Reject" |
| 697 | submitClass = "btn btn-danger" |
| 698 | eyebrow = "Pending · reject" |
| 699 | title = "Reject message" |
| 700 | lede = "This message will be discarded and not sent." |
| 701 | } |
| 702 | |
| 703 | bodyPreview := msg.BodyText |
| 704 | if bodyPreview == "" && msg.BodyHTML != "" { |
| 705 | bodyPreview = "(HTML only; view full message in the dashboard)" |
| 706 | } |
| 707 | |
| 708 | toList := strings.Join(msg.ToRecipients, ", ") |
| 709 | ccList := strings.Join(msg.CC, ", ") |
| 710 | bccList := strings.Join(msg.BCC, ", ") |
| 711 | |
| 712 | var rows strings.Builder |
| 713 | fmt.Fprintf(&rows, `<dt>Agent</dt><dd><code>%s</code></dd>`, |
| 714 | html.EscapeString(msg.AgentID)) |
| 715 | if toList != "" { |
| 716 | fmt.Fprintf(&rows, `<dt>To</dt><dd>%s</dd>`, html.EscapeString(toList)) |
| 717 | } |
| 718 | if ccList != "" { |
| 719 | fmt.Fprintf(&rows, `<dt>Cc</dt><dd>%s</dd>`, html.EscapeString(ccList)) |
| 720 | } |
| 721 | if bccList != "" { |
| 722 | fmt.Fprintf(&rows, `<dt>Bcc</dt><dd>%s</dd>`, html.EscapeString(bccList)) |
| 723 | } |
| 724 | fmt.Fprintf(&rows, `<dt>Subject</dt><dd><strong>%s</strong></dd>`, |
| 725 | html.EscapeString(msg.Subject)) |
| 726 | if msg.ApprovalExpiresAt != nil { |
| 727 | fmt.Fprintf(&rows, `<dt>Expires</dt><dd>%s</dd>`, |
| 728 | html.EscapeString(msg.ApprovalExpiresAt.UTC().Format(time.RFC1123))) |
| 729 | } |
| 730 | |
| 731 | // Optional rejection-reason input only on /reject. |
| 732 | reasonField := "" |
| 733 | if action == approvaltoken.ActionReject { |
| 734 | reasonField = `<div class="field"> |
| 735 | <label for="reason">Reason (optional)</label> |
| 736 | <input id="reason" name="reason" type="text" maxlength="200" placeholder="e.g. wrong tone, bad recipient"> |
| 737 | </div>` |
| 738 | } |
| 739 |
no test coverage detected