Documentation
¶
Overview ¶
Package comments lets visitors leave messages on the website: comments below news posts or pages, and guestbooks. Messages belong to a subject, a string the template chooses, such as a post ID, a page path or a fixed word. New messages wait for approval in the admin unless moderation is switched off; staff can reply from the admin. Add an instance per kind of conversation:
app.Use(
comments.Module("remarks", "Comments"),
comments.Module("guestbook", "Guestbook"),
)
Comments in any template ¶
The template functions work in every template, for example below a post of a Publishing instance:
{{range comments "remarks" .ID}}
<article>
<strong>{{.Name}}</strong> {{date .CreatedAt}}
{{.HTML}}
{{range .Replies}}
<blockquote>{{if .Staff}}Reply from the site:{{else}}{{.Name}}:{{end}} {{.HTML}}</blockquote>
{{end}}
</article>
{{end}}
<div id="remarks">
{{with commentResult "remarks"}}<p class="notice">{{.}}</p>{{end}}
{{if not (commentsClosed "remarks")}}
{{with commentInput "remarks" .ID}}
{{form_open .}}
{{form_field . "subject" nil}}
{{form_field . "name" nil}}
{{form_field . "email" nil}}
{{form_field . "body" nil}}
<button>Send</button>
{{form_close}}
{{end}}
{{end}}
</div>
Functions, all with the instance key first:
- comments key subject: the approved comments on subject, oldest first, each with its approved Replies (see Comment).
- commentCount key subject: the number of approved comments and replies.
- commentURL key: the address the form posts to.
- commentInput key subject: the form's model (PostInput) for go-form's functions, as above.
- commentResult key: the outcome of the visitor's last post (see below), or "".
- commentsClosed key: whether the instance accepts no new comments.
- commentThread key subject: the thread component, rendered (below).
- commentForm key subject: the form component, rendered (below).
Components ¶
Instead of writing the markup above, a template can include the module's components (see webthings.Module.Components), which come with plain, accessible default markup:
{{commentThread "remarks" .ID}} thread: the comments with their replies, then the form
{{commentForm "remarks" .ID}} form: the form with the result of the last post
thread gets a ThreadView, form a FormView. A theme replaces them with <key>/thread.gohtml and <key>/form.gohtml, or comments/thread.gohtml and comments/form.gohtml for every instance; App.WriteTemplates(dir, key) writes the defaults there to start from. The defaults use the classes wt-comments, wt-comments__title, wt-comments__list, wt-comments__empty, wt-comment, wt-comment--reply, wt-comment--staff, wt-comment__header, wt-comment__name, wt-comment__badge, wt-comment__date, wt-comment__body, wt-comment__replies and wt-comment-form, and the shared wt-form, wt-form__title, wt-form__field, wt-form__label, wt-form__input, wt-form__help, wt-notice, wt-meta and wt-button, styled by the optional {{asset "webthings.css"}}; their texts go through tl, so the site's translations translate them.
Form fields ¶
The form posts subject (required), name (required, at most 100 characters), email (optional, never shown on the website), body (required, plain text, at most 5000 characters) and optionally parent, the ID of the comment to reply to: PostInput, whose fields form_field renders with these limits. form_open adds the spam trap and the page to return to: the element with the instance key as its ID, and the fields' IDs start with the key, such as remarks-name. Each visitor can post 5 times per 10 minutes.
After a post the visitor returns to the page with ?<key>=<result>, which commentResult reads: "sent" (shown right away), "pending" (waiting for approval), "invalid" (a field is missing or too long), "closed" (the instance accepted no comments at that moment) or "throttled" (too many posts).
Importing ¶
Of(app, key).Add stores comments from another system: set Approved, and CreatedAt to keep their date; a reply has the ID of the Comment Add returned for its parent as ParentID.
Comment.CreatedAt is in UTC; {{date .CreatedAt}} and {{time .CreatedAt}} show it in the page's language and the site's timezone.
Guestbook page ¶
Placed on a page, an instance renders comments/list.gohtml (or <key>/list.gohtml) with a ListView: every approved message of the instance, newest first, 20 per page, and the details for a form posting with subject ListView.Subject (the instance key).
Settings ¶
The admin's settings screen per instance switches moderation (on by default) and closes the instance for new comments. With a notification address set, every new message sends an e-mail with a link to the admin: the mail template notify (see webthings.Module.Mail) with NotifyView as data, in the admin's language. A theme replaces it with mail/<key>/notify.gohtml or mail/comments/notify.gohtml.
Visitors are told apart for a day by an HMAC of their address with a key of the instance (webthings.Env.Secret), so the address is never stored.
Index ¶
- Constants
- Variables
- func Module(key, title string) webthings.Module
- type Comment
- type Entry
- type FormView
- type InboxGroup
- type InboxRow
- type InboxView
- type ListView
- type NotifyView
- type PostInput
- type ReplyView
- type Service
- func (s *Service) Add(ctx context.Context, entry Entry) (Comment, error)
- func (s *Service) Approve(ctx context.Context, id string, approved bool) error
- func (s *Service) Count(ctx context.Context, subject string) (int, error)
- func (s *Service) Delete(ctx context.Context, id string) error
- func (s *Service) Latest(ctx context.Context, offset, limit int) ([]Comment, bool, error)
- func (s *Service) List(ctx context.Context, subject string) ([]Comment, error)
- func (s *Service) Pending(ctx context.Context) (int, error)
- func (s *Service) Purge(ctx context.Context, id string) error
- func (s *Service) Reply(ctx context.Context, id, name, body string) (Comment, error)
- func (s *Service) SaveSettings(ctx context.Context, settings Settings) error
- func (s *Service) Settings(ctx context.Context) (Settings, error)
- func (s *Service) Undelete(ctx context.Context, id string) error
- type Settings
- type ThreadView
Constants ¶
const ( // ResultSent is a comment that shows at once. ResultSent = "sent" // ResultPending is a comment that waits for moderation. ResultPending = "pending" // ResultInvalid is a post with a missing or too long field; nothing // was saved. ResultInvalid = "invalid" // ResultClosed is a post to a post whose comments are closed. ResultClosed = "closed" // ResultThrottled is the answer to a visitor who posted too often. ResultThrottled = frontend.ResultThrottled )
Results of a visitor's post, added to the page as ?<key>=<result>.
Variables ¶
var ErrInvalid = errors.New("comments_invalid")
ErrInvalid is returned for a message with a missing or too long field, or a parent that does not exist.
var ErrParentDeleted = errors.New("comments_parent_deleted")
ErrParentDeleted is returned when restoring a reply whose comment is in the trash: restore the comment instead.
Functions ¶
Types ¶
type Comment ¶
type Comment struct {
ID string
Subject string
Name string
// Body is the plain text as written.
Body string
// HTML is Body escaped, with paragraphs and line breaks.
HTML template.HTML
// Staff is set for replies written in the admin.
Staff bool
Locale string
// CreatedAt is in UTC; {{date .CreatedAt}} shows it in the site's timezone.
CreatedAt time.Time
// Replies are the approved replies, oldest first. Replies have none.
Replies []Comment
}
Comment is an approved message as templates see it. The e-mail address is never part of it.
type Entry ¶
type Entry struct {
Subject string
// ParentID makes the entry a reply. A reply to a reply is attached to the
// top-level comment, so threads stay one level deep.
ParentID string
Name string
Email string
Body string
Locale string
// Path is the page the message was written on, for the admin.
Path string
Approved bool
Staff bool
// IPHash identifies the sender for a day; see Service.visitor.
IPHash string
// CreatedAt is when the message was written, for imports; zero is now.
CreatedAt time.Time
}
Entry is a new message, from a visitor or an import.
type FormView ¶
type FormView struct {
Key string
Subject string
// Action is the address the form posts to.
Action string
// Form is the form's model (PostInput) for go-form's template
// functions, such as {{form_open .Form}} and {{form_field .Form "name"
// nil}}; it returns the visitor to the element with the id Key.
Form form.RenderModel
// Result is "sent", "pending", "invalid", "closed", "throttled" or "".
Result string
// Closed is set when the instance accepts no new comments.
Closed bool
// Moderated is set when new comments wait for approval.
Moderated bool
}
FormView is the data of the form component: the comment form of a subject with the result of the visitor's last post.
type InboxGroup ¶
type InboxGroup struct {
Subject string
// Title names the subject for people: the instance title for the
// instance's own subject (a guestbook); otherwise the title of the
// published content on the page the messages were written on, in the
// admin's language when a message was written in it, or that page's
// address when no published content is there; the subject key when no
// message knows its page.
Title string
// Address is the page whose content Title names, shown with it.
Address string
Rows []InboxRow
}
InboxGroup is the messages on one subject.
type InboxRow ¶
type InboxRow struct {
Date string
Name string
Email string
Locale string
Body string
// PageURL is the page the message was written on.
PageURL string
ReplyTo string
Staff bool
Approved bool
// Sender is a short code that is equal for messages from one address on
// one day.
Sender string
Approve *form.RenderModel
Hide *form.RenderModel
Delete *form.RenderModel
ReplyURL string
}
InboxRow is one message in the admin list.
type InboxView ¶
type InboxView struct {
Title string
Pending int
// Filters switch between pending, approved and all messages.
Filters admin.Tabs
Groups []InboxGroup
SettingsURL string
TrashURL string
Pager admin.Pager
}
InboxView is the data of templates/admin/list.gohtml.
type ListView ¶
type ListView struct {
// Title is the title of the page the module is placed on.
Title string
// Key is the instance key, for the template functions.
Key string
// Comments are the approved messages of every subject, newest first, with
// their replies.
Comments []Comment
// Action is the address the form posts to.
Action string
// Subject is the value of the form's subject field.
Subject string
// Form is the form's model (PostInput) for go-form's template
// functions, such as {{form_open .Form}} and {{form_field .Form "name"
// nil}}; it returns the visitor to the element with the id Key.
Form form.RenderModel
// Result is the outcome of the visitor's last post: "sent", "pending",
// "invalid", "closed", "throttled" or "".
Result string
// Closed is set when the instance accepts no new messages.
Closed bool
// Moderated is set when new messages wait for approval.
Moderated bool
// NewerURL and OlderURL page through the messages; empty at either end.
NewerURL string
OlderURL string
}
ListView is the data of comments/list.gohtml, the instance placed on a page.
type NotifyView ¶
type NotifyView struct {
Comment
// Email is the visitor's address; empty when they gave none.
Email string
// Title is the instance's title, for example "Comments".
Title string
// Pending is set when the message waits for approval.
Pending bool
// PageURL is the page the message was written on; empty when unknown.
PageURL string
// ReviewURL opens the messages in the admin.
ReviewURL string
}
NotifyView is the data of the mail template notify, sent to the notification address in the admin's language. Replying answers the visitor when they gave an address.
type PostInput ¶
type PostInput struct {
Subject string `form:"input,hidden" name:"subject" required:"true" maxLength:"200" line:"true"`
Parent string `form:"input,hidden" name:"parent" maxLength:"64" line:"true"`
Name string `form:"input,text" name:"name" required:"true" maxLength:"100" line:"true" label:"Name" autocomplete:"name"`
Email string `` /* 191-byte string literal not displayed */
Body string `form:"textarea" name:"body" required:"true" maxLength:"5000" label:"Message"`
}
PostInput is the form a visitor posts to /_/<key>/post: the subject the message belongs to, optionally the message it answers, a name, an optional e-mail address and the message. Themes render its fields with form_field, with their limits ({{form_field .Form "name" nil}}, see ListView, FormView and commentInput), or write inputs with these names.
type ReplyView ¶
type ReplyView struct {
Title string
BackURL string
Name string
Date string
Body string
Form form.RenderModel
Errors form.FieldErrors
}
ReplyView is the data of templates/admin/reply.gohtml.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is one Comments instance, for the website's own code.
func (*Service) Add ¶
Add stores a message after checking its fields, for visitors and for imports: an import sets Approved, and CreatedAt for the original date, and passes the ID of the returned Comment as ParentID of its replies. It returns ErrInvalid for missing or too long fields and for a parent that is not a comment of this instance on the same subject.
func (*Service) Count ¶
Count returns the number of approved comments on subject, including replies to approved comments: the ones List shows.
func (*Service) Delete ¶
Delete moves the message with id to the trash, with its replies: they leave the website and the admin's lists, and Undelete brings them back. The replies get the same deleted_at, which is how Undelete knows which of them went along.
func (*Service) Latest ¶
Latest returns approved top-level messages of every subject, newest first, with their approved replies, skipping offset; and whether older ones exist. It is the guestbook page. A limit of 0 or less returns all of them.
func (*Service) List ¶
List returns the approved comments on subject, oldest first, with their approved replies.
func (*Service) Purge ¶
Purge removes the message with id from the trash for good, with all its replies. db.ErrNotFound means the message is not in the trash.
func (*Service) Reply ¶
Reply adds an approved staff reply to the comment with id, signed with name. Replying approves the comment too: the answer would be hidden otherwise.
func (*Service) SaveSettings ¶
SaveSettings replaces the instance's options. Notify must be empty or one e-mail address.
func (*Service) Settings ¶
Settings returns the instance's options; the defaults when none are saved.
type Settings ¶
type Settings struct {
// Moderated hides new messages until they are approved.
Moderated bool `db:"moderated"`
// Closed refuses new messages.
Closed bool `db:"closed"`
// Notify receives an e-mail for every new message when set.
Notify string `db:"notify"`
}
Settings are the options of one instance.
type ThreadView ¶
type ThreadView struct {
// Key is the instance key, for {{commentForm .Key .Subject}}.
Key string
Subject string
// Comments are oldest first, each with its approved Replies.
Comments []Comment
// Count is the number of approved comments and replies.
Count int
}
ThreadView is the data of the thread component: the approved comments on a subject, followed by the form (the form component).