From d954b7c8e3884fb032dc869ea93a3d18fc9daff6 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 13 Sep 2019 10:58:17 -0700 Subject: [PATCH 1/8] add user invite instructions this adds a new page with instructions for sharing user invites if a user clicks the link for one of their own invite codes they are directed to a page with clear instructions for it's use. if a user clicks another users link they are redirectec to their account settings witha flash telling them they do not need to register. --- database.go | 13 +++++++++++ invites.go | 29 +++++++++++++++++++++---- routes.go | 9 ++++---- templates/user/invite-instructions.tmpl | 25 +++++++++++++++++++++ 4 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 templates/user/invite-instructions.tmpl diff --git a/database.go b/database.go index 730bb60..a3235b6 100644 --- a/database.go +++ b/database.go @@ -2257,6 +2257,19 @@ func (db *datastore) GetUserInvite(id string) (*Invite, error) { return &i, nil } +// IsUsersInvite returns true if the user with ID created the invite with code +// and an error other than sql no rows, if any. Will return false in the event +// of an error. +func (db *datastore) IsUsersInvite(code string, userID int64) (bool, error) { + var id string + err := db.QueryRow("SELECT id FROM userinvites WHERE id = ? AND owner_id = ?", code, userID).Scan(&id) + if err != nil && err != sql.ErrNoRows { + log.Error("Failed selecting invite: %v", err) + return false, err + } + return id != "", nil +} + func (db *datastore) GetUsersInvitedCount(id string) int64 { var count int64 err := db.QueryRow("SELECT COUNT(*) FROM usersinvited WHERE invite_id = ?", id).Scan(&count) diff --git a/invites.go b/invites.go index 561255f..1e58ac3 100644 --- a/invites.go +++ b/invites.go @@ -12,15 +12,16 @@ package writefreely import ( "database/sql" + "html/template" + "net/http" + "strconv" + "time" + "github.com/gorilla/mux" "github.com/writeas/impart" "github.com/writeas/nerds/store" "github.com/writeas/web-core/log" "github.com/writeas/writefreely/page" - "html/template" - "net/http" - "strconv" - "time" ) type Invite struct { @@ -109,6 +110,26 @@ func handleCreateUserInvite(app *App, u *User, w http.ResponseWriter, r *http.Re func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { inviteCode := mux.Vars(r)["code"] + if u := getUserSession(app, r); u != nil { + // check if invite belongs to another user + // error can be ignored as not important in this case + if ownInvite, _ := app.db.IsUsersInvite(inviteCode, u.ID); !ownInvite { + addSessionFlash(app, w, r, "No need for an invite, You are already registered.", nil) + // show homepage + return impart.HTTPError{http.StatusFound, "/me/settings"} + } + // show invite instructions + p := struct { + *UserPage + InviteID string + }{ + UserPage: NewUserPage(app, r, u, "Invite Instructions", nil), + InviteID: inviteCode, + } + showUserPage(w, "invite-instructions", p) + return nil + } + i, err := app.db.GetUserInvite(inviteCode) if err != nil { return err diff --git a/routes.go b/routes.go index 777492b..0113e93 100644 --- a/routes.go +++ b/routes.go @@ -11,13 +11,14 @@ package writefreely import ( + "net/http" + "path/filepath" + "strings" + "github.com/gorilla/mux" "github.com/writeas/go-webfinger" "github.com/writeas/web-core/log" "github.com/writefreely/go-nodeinfo" - "net/http" - "path/filepath" - "strings" ) // InitStaticRoutes adds routes for serving static files. @@ -151,7 +152,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router { // Handle special pages first write.HandleFunc("/login", handler.Web(viewLogin, UserLevelNoneRequired)) write.HandleFunc("/signup", handler.Web(handleViewLanding, UserLevelNoneRequired)) - write.HandleFunc("/invite/{code}", handler.Web(handleViewInvite, UserLevelNoneRequired)).Methods("GET") + write.HandleFunc("/invite/{code}", handler.Web(handleViewInvite, UserLevelOptional)).Methods("GET") // TODO: show a reader-specific 404 page if the function is disabled write.HandleFunc("/read", handler.Web(viewLocalTimeline, UserLevelReader)) RouteRead(handler, UserLevelReader, write.PathPrefix("/read").Subrouter()) diff --git a/templates/user/invite-instructions.tmpl b/templates/user/invite-instructions.tmpl new file mode 100644 index 0000000..8bd2a30 --- /dev/null +++ b/templates/user/invite-instructions.tmpl @@ -0,0 +1,25 @@ +{{define "invite-instructions"}} +{{template "header" .}} + +
+

Invite Instructions

+

This is a special link that you can send to anyone you want to join {{ .SiteName }}. Copy the link below and paste it into an email, instant message, or text message and send it to the person you want. When they navigate to the link, they'll be able to create an account.

+ +
+ +{{template "footer" .}} +{{end}} From 891b15b8a8e077e27a19b9384d0bc4470b7bb5f6 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 23 Sep 2019 09:19:21 -0400 Subject: [PATCH 2/8] Always return invite errors This ensures we see a 404 page when looking up an invalid invite URL, even if the user is logged in. Ref T690 --- invites.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/invites.go b/invites.go index 1e58ac3..1bd75e6 100644 --- a/invites.go +++ b/invites.go @@ -110,6 +110,11 @@ func handleCreateUserInvite(app *App, u *User, w http.ResponseWriter, r *http.Re func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { inviteCode := mux.Vars(r)["code"] + i, err := app.db.GetUserInvite(inviteCode) + if err != nil { + return err + } + if u := getUserSession(app, r); u != nil { // check if invite belongs to another user // error can be ignored as not important in this case @@ -130,11 +135,6 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { return nil } - i, err := app.db.GetUserInvite(inviteCode) - if err != nil { - return err - } - p := struct { page.StaticPage Error string From 7e9e3cb7eb728d5da35da6bd9089a8b2c6bfd0b9 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 23 Sep 2019 09:45:36 -0400 Subject: [PATCH 3/8] Show status on logged-in expired invite links Ref T690 --- invites.go | 18 +++++++++++------- templates/user/invite-instructions.tmpl | 20 ++++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/invites.go b/invites.go index 1bd75e6..f7dcd7a 100644 --- a/invites.go +++ b/invites.go @@ -115,6 +115,13 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { return err } + expired := i.Expired() + if !expired && i.MaxUses.Valid && i.MaxUses.Int64 > 0 { + // Invite has a max-use number, so check if we're past that limit + i.uses = app.db.GetUsersInvitedCount(inviteCode) + expired = i.uses >= i.MaxUses.Int64 + } + if u := getUserSession(app, r); u != nil { // check if invite belongs to another user // error can be ignored as not important in this case @@ -123,13 +130,16 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { // show homepage return impart.HTTPError{http.StatusFound, "/me/settings"} } + // show invite instructions p := struct { *UserPage InviteID string + Expired bool }{ UserPage: NewUserPage(app, r, u, "Invite Instructions", nil), InviteID: inviteCode, + Expired: expired, } showUserPage(w, "invite-instructions", p) return nil @@ -145,16 +155,10 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { Invite: inviteCode, } - if i.Expired() { + if expired { p.Error = "This invite link has expired." } - if i.MaxUses.Valid && i.MaxUses.Int64 > 0 { - if c := app.db.GetUsersInvitedCount(inviteCode); c >= i.MaxUses.Int64 { - p.Error = "This invite link has expired." - } - } - // Get error messages session, err := app.sessionStore.Get(r, cookieName) if err != nil { diff --git a/templates/user/invite-instructions.tmpl b/templates/user/invite-instructions.tmpl index 8bd2a30..a70acd8 100644 --- a/templates/user/invite-instructions.tmpl +++ b/templates/user/invite-instructions.tmpl @@ -11,14 +11,18 @@

Invite Instructions

-

This is a special link that you can send to anyone you want to join {{ .SiteName }}. Copy the link below and paste it into an email, instant message, or text message and send it to the person you want. When they navigate to the link, they'll be able to create an account.

- + {{ if .Expired }} +

This invite link is expired.

+ {{ else }} +

This is a special link that you can send to anyone you want to join {{ .SiteName }}. Copy the link below and paste it into an email, instant message, or text message and send it to the person you want. When they navigate to the link, they'll be able to create an account.

+ + {{ end }}
{{template "footer" .}} From f01b439ff531b88230640b78ae6bd7e1a1210d36 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 23 Sep 2019 10:02:36 -0400 Subject: [PATCH 4/8] Tweak invite page title and intro Ref T690 --- invites.go | 2 +- templates/user/invite-instructions.tmpl | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/invites.go b/invites.go index f7dcd7a..f9c5cce 100644 --- a/invites.go +++ b/invites.go @@ -137,7 +137,7 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { InviteID string Expired bool }{ - UserPage: NewUserPage(app, r, u, "Invite Instructions", nil), + UserPage: NewUserPage(app, r, u, "Invite to "+app.cfg.App.SiteName, nil), InviteID: inviteCode, Expired: expired, } diff --git a/templates/user/invite-instructions.tmpl b/templates/user/invite-instructions.tmpl index a70acd8..c99680a 100644 --- a/templates/user/invite-instructions.tmpl +++ b/templates/user/invite-instructions.tmpl @@ -10,18 +10,12 @@ }
-

Invite Instructions

+

Invite to {{.SiteName}}

{{ if .Expired }}

This invite link is expired.

{{ else }} -

This is a special link that you can send to anyone you want to join {{ .SiteName }}. Copy the link below and paste it into an email, instant message, or text message and send it to the person you want. When they navigate to the link, they'll be able to create an account.

- +

Copy the link below and send it to anyone that you want to join {{ .SiteName }}. You could paste it into an email, instant message, text message, or write it down on paper. Anyone who navigates to this special page will be able to create an account.

+ {{ end }}
From 26a4f48e8b3b33fbfd79cc207052364fd9f5e08d Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 23 Sep 2019 10:04:45 -0400 Subject: [PATCH 5/8] Add expiration information to invite help This uses the Invite fetched from the database to explain a bit more about how the invite URL expires. It also reduces some space around the input box. Ref T690 --- invites.go | 4 ++-- templates/user/invite-instructions.tmpl | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/invites.go b/invites.go index f9c5cce..2320906 100644 --- a/invites.go +++ b/invites.go @@ -134,11 +134,11 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { // show invite instructions p := struct { *UserPage - InviteID string + Invite *Invite Expired bool }{ UserPage: NewUserPage(app, r, u, "Invite to "+app.cfg.App.SiteName, nil), - InviteID: inviteCode, + Invite: i, Expired: expired, } showUserPage(w, "invite-instructions", p) diff --git a/templates/user/invite-instructions.tmpl b/templates/user/invite-instructions.tmpl index c99680a..a32eac0 100644 --- a/templates/user/invite-instructions.tmpl +++ b/templates/user/invite-instructions.tmpl @@ -3,7 +3,7 @@ From c6564b3d168fdaa7813aa9ce442f35fd8a89d562 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 23 Sep 2019 10:31:38 -0400 Subject: [PATCH 7/8] Shorten invite-instructions.tmpl filename --- invites.go | 2 +- templates/user/{invite-instructions.tmpl => invite-help.tmpl} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename templates/user/{invite-instructions.tmpl => invite-help.tmpl} (97%) diff --git a/invites.go b/invites.go index 2320906..3e19d7e 100644 --- a/invites.go +++ b/invites.go @@ -141,7 +141,7 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error { Invite: i, Expired: expired, } - showUserPage(w, "invite-instructions", p) + showUserPage(w, "invite-help", p) return nil } diff --git a/templates/user/invite-instructions.tmpl b/templates/user/invite-help.tmpl similarity index 97% rename from templates/user/invite-instructions.tmpl rename to templates/user/invite-help.tmpl index e31e778..978cfad 100644 --- a/templates/user/invite-instructions.tmpl +++ b/templates/user/invite-help.tmpl @@ -1,4 +1,4 @@ -{{define "invite-instructions"}} +{{define "invite-help"}} {{template "header" .}}