Merge pull request #183 from writeas/T690-invite-instructions
add user invite instructions Resolves T690
This commit is contained in:
commit
3759f16ed3
4 changed files with 86 additions and 15 deletions
13
database.go
13
database.go
|
@ -2257,6 +2257,19 @@ func (db *datastore) GetUserInvite(id string) (*Invite, error) {
|
||||||
return &i, nil
|
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 {
|
func (db *datastore) GetUsersInvitedCount(id string) int64 {
|
||||||
var count int64
|
var count int64
|
||||||
err := db.QueryRow("SELECT COUNT(*) FROM usersinvited WHERE invite_id = ?", id).Scan(&count)
|
err := db.QueryRow("SELECT COUNT(*) FROM usersinvited WHERE invite_id = ?", id).Scan(&count)
|
||||||
|
|
47
invites.go
47
invites.go
|
@ -12,15 +12,16 @@ package writefreely
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/writeas/impart"
|
"github.com/writeas/impart"
|
||||||
"github.com/writeas/nerds/store"
|
"github.com/writeas/nerds/store"
|
||||||
"github.com/writeas/web-core/log"
|
"github.com/writeas/web-core/log"
|
||||||
"github.com/writeas/writefreely/page"
|
"github.com/writeas/writefreely/page"
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Invite struct {
|
type Invite struct {
|
||||||
|
@ -114,6 +115,36 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error {
|
||||||
return err
|
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
|
||||||
|
if ownInvite, _ := app.db.IsUsersInvite(inviteCode, u.ID); !ownInvite {
|
||||||
|
addSessionFlash(app, w, r, "You're already registered and logged in.", nil)
|
||||||
|
// show homepage
|
||||||
|
return impart.HTTPError{http.StatusFound, "/me/settings"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// show invite instructions
|
||||||
|
p := struct {
|
||||||
|
*UserPage
|
||||||
|
Invite *Invite
|
||||||
|
Expired bool
|
||||||
|
}{
|
||||||
|
UserPage: NewUserPage(app, r, u, "Invite to "+app.cfg.App.SiteName, nil),
|
||||||
|
Invite: i,
|
||||||
|
Expired: expired,
|
||||||
|
}
|
||||||
|
showUserPage(w, "invite-help", p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
p := struct {
|
p := struct {
|
||||||
page.StaticPage
|
page.StaticPage
|
||||||
Error string
|
Error string
|
||||||
|
@ -124,16 +155,10 @@ func handleViewInvite(app *App, w http.ResponseWriter, r *http.Request) error {
|
||||||
Invite: inviteCode,
|
Invite: inviteCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.Expired() {
|
if expired {
|
||||||
p.Error = "This invite link has 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
|
// Get error messages
|
||||||
session, err := app.sessionStore.Get(r, cookieName)
|
session, err := app.sessionStore.Get(r, cookieName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
package writefreely
|
package writefreely
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/writeas/go-webfinger"
|
"github.com/writeas/go-webfinger"
|
||||||
"github.com/writeas/web-core/log"
|
"github.com/writeas/web-core/log"
|
||||||
"github.com/writefreely/go-nodeinfo"
|
"github.com/writefreely/go-nodeinfo"
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitStaticRoutes adds routes for serving static files.
|
// InitStaticRoutes adds routes for serving static files.
|
||||||
|
@ -151,7 +152,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
|
||||||
// Handle special pages first
|
// Handle special pages first
|
||||||
write.HandleFunc("/login", handler.Web(viewLogin, UserLevelNoneRequired))
|
write.HandleFunc("/login", handler.Web(viewLogin, UserLevelNoneRequired))
|
||||||
write.HandleFunc("/signup", handler.Web(handleViewLanding, 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
|
// TODO: show a reader-specific 404 page if the function is disabled
|
||||||
write.HandleFunc("/read", handler.Web(viewLocalTimeline, UserLevelReader))
|
write.HandleFunc("/read", handler.Web(viewLocalTimeline, UserLevelReader))
|
||||||
RouteRead(handler, UserLevelReader, write.PathPrefix("/read").Subrouter())
|
RouteRead(handler, UserLevelReader, write.PathPrefix("/read").Subrouter())
|
||||||
|
|
32
templates/user/invite-help.tmpl
Normal file
32
templates/user/invite-help.tmpl
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{{define "invite-help"}}
|
||||||
|
{{template "header" .}}
|
||||||
|
<style>
|
||||||
|
.copy-link {
|
||||||
|
width: 100%;
|
||||||
|
margin: 1rem 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="snug content-container">
|
||||||
|
<h1>Invite to {{.SiteName}}</h1>
|
||||||
|
{{ if .Expired }}
|
||||||
|
<p style="font-style: italic">This invite link is expired.</p>
|
||||||
|
{{ else }}
|
||||||
|
<p>Copy the link below and send it to anyone that you want to join <em>{{ .SiteName }}</em>. 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.</p>
|
||||||
|
<input class="copy-link" type="text" name="invite-url" value="{{$.Host}}/invite/{{.Invite.ID}}" onfocus="if (this.select) this.select(); else this.setSelectionRange(0, this.value.length);" readonly />
|
||||||
|
<p>
|
||||||
|
{{ if gt .Invite.MaxUses.Int64 0 }}
|
||||||
|
{{if eq .Invite.MaxUses.Int64 1}}Only <strong>one</strong> user{{else}}Up to <strong>{{.Invite.MaxUses.Int64}}</strong> users{{end}} can sign up with this link.
|
||||||
|
{{if gt .Invite.Uses 0}}So far, <strong>{{.Invite.Uses}}</strong> {{pluralize "person has" "people have" .Invite.Uses}} used it.{{end}}
|
||||||
|
{{if .Invite.Expires}}It expires on <strong>{{.Invite.ExpiresFriendly}}</strong>.{{end}}
|
||||||
|
{{ else }}
|
||||||
|
It can be used as many times as you like{{if .Invite.Expires}} before <strong>{{.Invite.ExpiresFriendly}}</strong>, when it expires{{end}}.
|
||||||
|
{{ end }}
|
||||||
|
</p>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "footer" .}}
|
||||||
|
{{end}}
|
Loading…
Add table
Reference in a new issue