Documentation
¶
Overview ¶
Package registrations lets visitors sign up for events, or for any other subject: places with an optional capacity, adults and children with prices, a deadline, a confirmation e-mail with a personal code, and a page where visitors look up and cancel their registration. Editors open registration per subject, follow sign-ups and payments, and export them as CSV.
Link an instance to an events instance so editors pick events in the admin, and registration closes when an event starts:
app.Use(
events.Module("agenda", "Agenda"),
registrations.Module("signup", "Sign-ups", registrations.ForEvents("agenda")),
)
Without ForEvents, editors add subjects by ID (any text up to 64 characters, such as a page or product ID) and name.
Registering ¶
The sign-up form can sit on any page, typically events/event.gohtml. It posts to the module's action (registerURL) with these fields (SignupInput):
subject the subject ID, for example the event's .ID (required) name the visitor's name (required) email the visitor's e-mail address (required) adults number of adults, at least 1; 1 when the field is missing children number of children; 0 when missing, only when allowed notes optional remarks for the organiser
Availability.Form is its model for go-form's functions, with the adults and children at most the places left; form_open adds the spam trap and the return address, the element with the instance key as its id. A minimal form:
{{with registration "signup" .ID}}
<div id="signup">
{{if .Open}}
{{form_open .Form}}
{{form_field .Form "subject" nil}}
{{form_field .Form "name" nil}}
{{form_field .Form "email" nil}}
{{form_field .Form "adults" nil}}
{{if .AllowChildren}}{{form_field .Form "children" nil}}{{end}}
<button>Register</button>
{{form_close}}
{{if .Capacity}}<p>{{.Left}} places left</p>{{end}}
{{else if .Full}}<p>This event is full.</p>
{{else if .Configured}}<p>Registration is closed.</p>{{end}}
</div>
{{end}}
A successful registration sends a confirmation e-mail with the code, the total, the payment link and a link to the registration page. The visitor then lands on that page (/<page>/<code>?<key>=registered) when the module is placed on a page, and otherwise back on the form's page with ?<key>=registered&code=<code> (registrationCode reads it). Other results return to the form's page: ?<key>=full (not enough places left), ?<key>=closed (registration is not open), ?<key>=invalid (missing or wrong fields) or ?<key>=throttled (too many posts from the visitor). registrationResult reads the result.
Component ¶
Instead of writing the form, a template can include the form component (see webthings.Module.Components): the sign-up box of a subject with plain, accessible default markup: prices, places left, the deadline, the result of the visitor's last post (with the code) and the form, or a notice when the subject is full or closed. Subjects without registration settings render nothing.
{{registrationForm "signup" .ID}}
It gets a FormView. A theme replaces it with <key>/form.gohtml, or registrations/form.gohtml for every instance; App.WriteTemplates(dir, key) writes the default there to start from. The default uses the classes wt-registration, wt-registration__title, wt-registration__facts and wt-registration__notes with the shared wt-form, wt-form__field, wt-form__row, wt-notice and wt-button, styled by the optional {{asset "webthings.css"}}; its texts go through tl, so the site's translations translate them.
Looking up and cancelling ¶
Place the module on a page to let visitors find their registration by code. Below the page it serves:
/ registrations/lookup.gohtml LookupView: a form asking for the code (?code=)
/{code} registrations/registration.gohtml RegistrationView: one registration
Every answer below the page, "not found" for a mistyped code included, is sent with "Cache-Control: private, no-store" and "X-Robots-Tag: noindex": the address holds the code and the page personal details, so no cache keeps them, no search engine indexes them, and neither the stats module nor the missed paths of the redirects module record them.
The cancel form posts to RegistrationView.CancelAction with the fields code and email (the address used to register, as confirmation): CancelInput, whose model is RegistrationView.Form. It returns to the element with the instance key as its id, with ?<key>=cancelled, or ?<key>=invalid when the address does not match.
Template functions ¶
{{with registration "signup" .ID}}…{{end}} Availability of a subject in the page's language
{{registerURL "signup"}} the address sign-up forms post to
{{registrationResult "signup"}} the result of the visitor's last post on this page
{{registrationCode "signup"}} the code after registering on a page without the module
{{registrationForm "signup" .ID}} the form component, rendered
Amounts are Cents in the instance's currency (Currency, EUR by default): {{money .Prices.Adult .Currency}} shows € 12,50 on a Dutch page.
Mail ¶
The confirmation and the cancellation are mail templates (see webthings.Module.Mail) named confirmation and cancellation, with MailView as data, in the visitor's language. The defaults are English text in tl, so the site's translations translate them; a theme replaces them with mail/<key>/<name>.gohtml or mail/registrations/<name>.gohtml. Editors write the notes for registrants and the payment link per language.
Trash ¶
Deleting a registration in the admin moves it to the admin's trash: it no longer takes places and leaves the lists, the counts, the CSV export and the lookup by code. Restoring it fails when its places are no longer free. Cancelling is something else: a cancelled registration stays in the list.
Index ¶
- Constants
- Variables
- func Module(key, title string, opts ...Option) webthings.Module
- type Availability
- type CancelInput
- type Cents
- type Fact
- type FormView
- type LookupInput
- type LookupView
- type MailView
- type NewSubjectView
- type Option
- type OverviewRow
- type OverviewView
- type PaymentStatus
- type Prices
- type Registration
- type RegistrationAdminView
- type RegistrationRow
- type RegistrationView
- type Service
- func (s *Service) AllSettings(ctx context.Context) ([]Settings, error)
- func (s *Service) Availability(ctx context.Context, subjectID, locale string) (Availability, error)
- func (s *Service) ByCode(ctx context.Context, code string) (Registration, error)
- func (s *Service) Cancel(ctx context.Context, id string) error
- func (s *Service) Currency() string
- func (s *Service) Delete(ctx context.Context, id string) error
- func (s *Service) Purge(ctx context.Context, id string) error
- func (s *Service) Register(ctx context.Context, signup Signup) (Registration, error)
- func (s *Service) Registration(ctx context.Context, id string) (Registration, error)
- func (s *Service) Registrations(ctx context.Context, subjectID string) ([]Registration, error)
- func (s *Service) SaveSettings(ctx context.Context, settings Settings) error
- func (s *Service) Settings(ctx context.Context, subjectID string) (Settings, error)
- func (s *Service) Subject(ctx context.Context, subjectID, locale string) (Subject, error)
- func (s *Service) Summaries(ctx context.Context) (map[string]Summary, error)
- func (s *Service) Trashed(ctx context.Context, limit, offset int) ([]Registration, int, error)
- func (s *Service) Undelete(ctx context.Context, id string) error
- func (s *Service) Update(ctx context.Context, id string, payment PaymentStatus, adminNotes string) error
- type Settings
- type SettingsView
- type Signup
- type SignupInput
- type Subject
- type SubjectText
- type SubjectView
- type Summary
Constants ¶
const ( // ResultRegistered is a sign-up that was taken. ResultRegistered = "registered" // ResultFull is a sign-up for more places than are left. ResultFull = "full" // ResultClosed is a sign-up for an event that takes none (any more). ResultClosed = "closed" // ResultInvalid is a sign-up with a missing or wrong field. ResultInvalid = "invalid" // ResultCancelled is a registration the visitor cancelled. ResultCancelled = "cancelled" // ResultThrottled is the answer to a visitor who posted too often. ResultThrottled = frontend.ResultThrottled )
Results of visitor posts, sent back as ?<key>=<result>.
Variables ¶
var ( // ErrNotFound is returned when a subject or registration does not exist. ErrNotFound = db.ErrNotFound // ErrInvalid is returned for sign-ups with missing or wrong fields. ErrInvalid = errors.New("registrations_invalid") // ErrClosed is returned when registration is not open for the subject. ErrClosed = errors.New("registrations_closed") // ErrFull is returned when not enough places are left. ErrFull = errors.New("registrations_full") // ErrNoPlaces is returned when a registration in the trash cannot be // restored because its places were taken meanwhile. ErrNoPlaces = errors.New("registrations_no_places") )
var ErrNoEvents = errors.New("registrations_no_events")
ErrNoEvents is returned when the instance is linked to a key (ForEvents) that names no events instance.
Functions ¶
Types ¶
type Availability ¶
type Availability struct {
SubjectID string
// Configured is true when editors set up registration for the subject,
// open or not; without it a page shows no sign-up at all.
Configured bool
// Open is true while visitors can register: registration is switched on,
// the deadline has not passed and places are left.
Open bool
// Closed is true when registration is switched off or its deadline
// passed, and for subjects that are not Configured.
Closed bool
// Full is true when every place is taken.
Full bool
// Capacity is the number of places; 0 is unlimited.
Capacity int
// Taken is the number of places registered so far.
Taken int
// Left is the number of places left; 0 when Capacity is 0.
Left int
// ClosesAt is the deadline in the site's timezone; zero when there is none.
ClosesAt time.Time
AllowChildren bool
Prices Prices
// Currency is the ISO 4217 code of the prices, for {{money .Prices.Adult .Currency}}.
Currency string
// Notes are the editor's notes for registrants in the requested language,
// or in the site's default language, rendered from Markdown.
Notes template.HTML
// Action is the address the sign-up form posts to.
Action string
// Form is the sign-up form's model (SignupInput) for go-form's
// template functions, such as {{form_open .Form}} and {{form_field
// .Form "name" nil}}, with the adults and children at most Left when
// places are limited; it returns the visitor to the element with the
// instance key as its id.
Form form.RenderModel
}
Availability tells templates whether and how visitors can register for a subject.
type CancelInput ¶
type CancelInput struct {
Code string `form:"input,hidden" name:"code" required:"true" maxLength:"20" line:"true"`
Email string `` /* 143-byte string literal not displayed */
}
CancelInput is the cancel form a visitor posts to /_/<key>/cancel: the registration's code and the e-mail address it was made with.
type Cents ¶
type Cents int64
Cents is an amount of money in hundredths, for example 1250 for 12.50. Templates show it with money, for example {{money .Total .Currency}}.
func ParseCents ¶
ParseCents reads an amount such as "12", "12.5", "12.50" or "12,50". Empty is zero.
type FormView ¶
type FormView struct {
// Key is the instance key.
Key string
Availability
// Result is "registered", "full", "closed", "invalid", "cancelled",
// "throttled" or "".
Result string
// Code is the visitor's registration code after registering here, when
// the instance is placed on no page of its own.
Code string
// LookupURL is the page where visitors look up their registration, or ""
// when the instance is placed on no page.
LookupURL string
}
FormView is the data of the form component: whether and how visitors can register for a subject, with the result of the visitor's last post.
type LookupInput ¶
type LookupInput struct {
Code string `` /* 141-byte string literal not displayed */
}
LookupInput is the lookup form, LookupView.Form: the code from the confirmation, sent with method get as ?code=. Its field's id is registration-code.
type LookupView ¶
type LookupView struct {
// Title is the title of the page the module is mounted on.
Title string
// Action is the address the form sends its code field to, with method get.
Action string
// Code is the code that was looked up, if any.
Code string
// NotFound is set when no registration has Code.
NotFound bool
// Form is the lookup form's model (LookupInput, with Code), sent to
// Action with method get, for go-form's template functions, such as
// {{form_open .Form}} and {{form_field .Form "code" nil}}.
Form form.RenderModel
}
LookupView is the data of registrations/lookup.gohtml: a form where visitors enter their code.
type MailView ¶
type MailView struct {
// Subject is what the visitor registered for; Start is set for events.
Subject Subject
Code string
Name string
Adults int
Children int
// Total is in Currency, for {{money .Total .Currency}}.
Total Cents
Currency string
// Payment is none, pending, paid or cancelled.
Payment PaymentStatus
// PaymentLink is the editor's payment link while payment is pending.
PaymentLink string
// Notes are the visitor's own remarks; Info the editor's notes for
// registrants, as the Markdown they wrote, which reads well as text.
Notes string
Info string
// URL is the registration's page, to view or cancel it; empty when the
// module is on no page.
URL string
}
MailView is the data of the mail templates confirmation and cancellation, in the language the visitor registered in.
type NewSubjectView ¶
type NewSubjectView struct {
Title string
BackURL string
Form form.RenderModel
Errors form.FieldErrors
}
NewSubjectView adds a subject that is not an event.
type Option ¶
type Option func(*options)
Option configures an instance.
type OverviewRow ¶
type OverviewRow struct {
URL string
Title string
Date string
// Status is "Open", "Closed" or "Full"; it is translated.
Status string
Registrations int
Places string
}
OverviewRow is one event or subject in the overview, with its number of registrations and the places taken, out of the capacity when there is one.
type OverviewView ¶
type OverviewView struct {
Title string
// Events is set when the subjects are events; rows then have a date.
Events bool
// NewURL adds a subject; empty for events and without the right.
NewURL string
// TrashURL shows the deleted registrations in the admin's trash.
TrashURL string
Rows []OverviewRow
}
OverviewView lists the subjects with their registrations.
type PaymentStatus ¶
type PaymentStatus string
PaymentStatus tells whether a registration was paid.
const ( // PaymentNone is the status of free registrations. PaymentNone PaymentStatus = "none" // PaymentPending is the status of new registrations with a total. PaymentPending PaymentStatus = "pending" // PaymentPaid is set by an editor once the payment came in. PaymentPaid PaymentStatus = "paid" // PaymentCancelled is set when an unpaid registration is cancelled. PaymentCancelled PaymentStatus = "cancelled" )
func (PaymentStatus) Valid ¶
func (p PaymentStatus) Valid() bool
Valid reports whether status is one of the known statuses.
type Registration ¶
type Registration struct {
ID string `db:"id"`
SubjectID string `db:"subject_id"`
// Code is the visitor's reference, for example "K7Q4-9M2X".
Code string `db:"code"`
Name string `db:"name"`
Email string `db:"email"`
Adults int `db:"adults"`
Children int `db:"children"`
Total Cents `db:"total"`
Payment PaymentStatus `db:"payment_status"`
Notes string `db:"notes"`
AdminNotes string `db:"admin_notes"`
// Locale is the language the visitor registered in.
Locale string `db:"locale"`
CreatedAt time.Time `db:"created_at"`
CancelledAt *time.Time `db:"cancelled_at"`
// CreatedBy is the editor who added the registration; empty for visitors.
CreatedBy db.Ref `db:"created_by"`
// UpdatedAt and UpdatedBy are the last change, by an editor or, when
// cancelling, by the visitor (UpdatedBy empty).
UpdatedAt time.Time `db:"updated_at"`
UpdatedBy db.Ref `db:"updated_by"`
// DeletedAt is set while the registration is in the trash, with
// DeletedBy.
DeletedAt *time.Time `db:"deleted_at"`
DeletedBy db.Ref `db:"deleted_by"`
}
Registration is one sign-up.
func (Registration) Cancelled ¶
func (r Registration) Cancelled() bool
Cancelled reports whether the registration was cancelled.
func (Registration) Places ¶
func (r Registration) Places() int
Places is the number of places the registration takes.
type RegistrationAdminView ¶
type RegistrationAdminView struct {
Title string
BackURL string
Registration Registration
// Total is the amount in the admin language, such as "€ 120,00";
// Payment is the translated payment status.
Total string
Payment string
Date string
Cancelled string
// Form changes payment and admin notes; nil without the right.
Form *form.RenderModel
Errors form.FieldErrors
Cancel *form.RenderModel
Delete *form.RenderModel
}
RegistrationAdminView shows one registration to editors.
type RegistrationRow ¶
type RegistrationRow struct {
URL string
Code string
Name string
Email string
Adults int
Children int
Total string
Payment string
Date string
Cancelled bool
}
RegistrationRow is one registration in the list of an event or subject; Cancelled registrations stay in the list.
type RegistrationView ¶
type RegistrationView struct {
// Key is the instance key; results arrive as ?<key>=<result>, and the
// cancel form returns the visitor to the element with the id Key.
Key string
// Title is the title of the page the module is mounted on.
Title string
// Subject is what the visitor registered for; Start is set for events.
Subject Subject
Code string
Name string
Adults int
Children int
// Total is in Currency, for {{money .Total .Currency}}.
Total Cents
Currency string
// Payment is none, pending, paid or cancelled.
Payment PaymentStatus
// PaymentLink is the editor's payment link while payment is pending.
PaymentLink string
// Notes are the visitor's own remarks; Info the editor's notes for
// registrants, in the page's language, rendered from Markdown.
Notes string
Info template.HTML
// CreatedAt is when the visitor registered, for {{date .CreatedAt}}.
CreatedAt time.Time
Cancelled bool
// CancelAction is the address the cancel form posts to (fields code and
// email); empty when the registration is cancelled.
CancelAction string
// Form is the cancel form's model (CancelInput, with Code) for go-form's
// template functions, such as {{form_open .Form}} and {{form_field
// .Form "email" nil}}; the zero value when the registration is
// cancelled.
Form form.RenderModel
// Result is the outcome of the visitor's last post: "registered",
// "cancelled", "invalid", "throttled" or empty.
Result string
}
RegistrationView is the data of registrations/registration.gohtml.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is one Registrations instance, for the website's own code.
func (*Service) AllSettings ¶
AllSettings returns the settings of every subject, by title, without their texts.
func (*Service) Availability ¶
Availability tells whether visitors can register for a subject now, with the notes for registrants in locale. A subject without settings is closed and not Configured.
func (*Service) ByCode ¶
ByCode returns the registration with code, or ErrNotFound; also when it is in the trash. The code may be typed loosely, for example "k7q4 9m2x".
func (*Service) Cancel ¶
Cancel cancels a registration, freeing its places. Unpaid registrations get payment status cancelled; paid ones stay paid so they can be refunded. Cancelling twice is harmless; a registration in the trash is ErrNotFound.
func (*Service) Currency ¶
Currency is the ISO 4217 code of the instance's prices, for example "EUR".
func (*Service) Delete ¶
Delete moves a registration to the trash, freeing its places. One that is unknown or already in the trash is ErrNotFound.
func (*Service) Purge ¶
Purge removes a registration in the trash for good; one that is not in the trash is ErrNotFound.
func (*Service) Register ¶
Register signs a visitor up. It returns ErrInvalid for incomplete sign-ups, ErrClosed when registration is not open and ErrFull when not enough places are left. Concurrent sign-ups for one subject never exceed its capacity.
func (*Service) Registration ¶
Registration returns one registration, or ErrNotFound; also when it is in the trash.
func (*Service) Registrations ¶
Registrations returns every registration of a subject, cancelled ones included and those in the trash left out, in the order they came in.
func (*Service) SaveSettings ¶
SaveSettings creates or replaces the settings of a subject.
func (*Service) Settings ¶
Settings returns the settings of a subject with its texts, or ErrNotFound.
func (*Service) Subject ¶
Subject describes a subject in locale: the editor's title, or the event's.
func (*Service) Trashed ¶
Trashed returns registrations in the trash of every subject, most recently deleted first: at most limit of them from offset (all for a limit of 0), and how many the trash holds in all.
type Settings ¶
type Settings struct {
SubjectID string `db:"subject_id"`
// Title names the subject in the admin and e-mail. Events use their own
// title when it is empty.
Title string `db:"title"`
// Open switches registration on.
Open bool `db:"is_open"`
// Capacity is the number of places (adults and children); 0 is unlimited.
Capacity int `db:"capacity"`
// ClosesAt is the registration deadline. Without one, registration for an
// event closes when it starts.
ClosesAt *time.Time `db:"closes_at"`
AdultPrice Cents `db:"adult_price"`
ChildPrice Cents `db:"child_price"`
AllowChildren bool `db:"allow_children"`
// UpdatedAt and UpdatedBy are the last change by an editor.
UpdatedAt time.Time `db:"updated_at"`
UpdatedBy db.Ref `db:"updated_by"`
// Texts are what registrants read, per language. SaveSettings stores the
// languages given and keeps the others.
Texts []SubjectText
}
Settings decide whether and how visitors can register for one subject.
type SettingsView ¶
type SettingsView struct {
Title string
BackURL string
Form form.RenderModel
Errors form.FieldErrors
// Tabs switch the language of Texts, what registrants read.
Tabs []admin.LocaleTab
Texts form.RenderModel
TextErrors form.FieldErrors
}
SettingsView edits the settings of a subject.
type Signup ¶
type Signup struct {
SubjectID string
Name string
Email string
Adults int
Children int
Notes string
Locale string
}
Signup is what a visitor submits.
type SignupInput ¶
type SignupInput struct {
Subject string `form:"input,hidden" name:"subject" required:"true" maxLength:"64" pattern:"^[A-Za-z0-9_.:\\-]+$"`
Name string `` /* 154-byte string literal not displayed */
Email string `` /* 171-byte string literal not displayed */
Adults int `` /* 188-byte string literal not displayed */
Children int `` /* 188-byte string literal not displayed */
Notes string `` /* 241-byte string literal not displayed */
}
SignupInput is the sign-up form a visitor posts to /_/<key>/register: the subject, a name, an e-mail address, the number of adults (1 when empty) and children, and remarks. Themes render its fields with form_field, with their limits (Availability.Form), or write inputs with these names.
type Subject ¶
type Subject struct {
ID string
Title string
// Start is when an event starts and End when it ends, in the event's
// timezone; zero for other subjects. End is zero when the event has none,
// and the last day of all-day events.
Start time.Time
End time.Time
AllDay bool
// URL is the event's page, when visitors can see it.
URL string
}
Subject is what visitors register for, as the admin and e-mail show it.
type SubjectText ¶
type SubjectText struct {
Locale string `db:"locale"`
// Notes are shown with the sign-up form, on the registration page and in
// the confirmation. They are Markdown.
Notes string `db:"notes"`
// PaymentLink is shown while payment is pending, for example a payment
// request URL or bank details.
PaymentLink string `db:"payment_link"`
}
SubjectText is what registrants of a subject read in one language.
type SubjectView ¶
type SubjectView struct {
Title string
Date string
BackURL string
// BackLabel names the overview BackURL leads to, as a translation key.
BackLabel string
// SettingsURL is empty without the right to change settings.
SettingsURL string
// ExportURL is empty without the right to export.
ExportURL string
Status string
Facts []Fact
Rows []RegistrationRow
// Registrations, Places and Total count active registrations.
Registrations int
Places int
Total string
}
SubjectView shows one subject and its registrations.