mirror of
https://github.com/writefreely/writefreely
synced 2024-11-24 09:33:11 +00:00
address PR comments
- update error messages to be correct - move suspended message into template and include for other pages - check suspended status on all relevant pages and show message if logged in user is suspended. - fix possible nil pointer error - remove changes to db schema files - add version comment to migration - add UserStatus type with UserActive and UserSuspended - change database table to use status column instead of suspended - update toggle suspended handler to be toggle status in prep for possible future inclusion of further user statuses
This commit is contained in:
parent
9873fc443f
commit
f85f0751a3
30 changed files with 148 additions and 72 deletions
29
account.go
29
account.go
|
@ -750,14 +750,20 @@ func viewArticles(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||
log.Error("unable to fetch collections: %v", err)
|
||||
}
|
||||
|
||||
suspended, err := app.db.IsUserSuspended(u.ID)
|
||||
if err != nil {
|
||||
log.Error("view articles: %v", err)
|
||||
}
|
||||
d := struct {
|
||||
*UserPage
|
||||
AnonymousPosts *[]PublicPost
|
||||
Collections *[]Collection
|
||||
Suspended bool
|
||||
}{
|
||||
UserPage: NewUserPage(app, r, u, u.Username+"'s Posts", f),
|
||||
AnonymousPosts: p,
|
||||
Collections: c,
|
||||
Suspended: suspended,
|
||||
}
|
||||
d.UserPage.SetMessaging(u)
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
|
@ -779,6 +785,11 @@ func viewCollections(app *App, u *User, w http.ResponseWriter, r *http.Request)
|
|||
uc, _ := app.db.GetUserCollectionCount(u.ID)
|
||||
// TODO: handle any errors
|
||||
|
||||
suspended, err := app.db.IsUserSuspended(u.ID)
|
||||
if err != nil {
|
||||
log.Error("view collections %v", err)
|
||||
return fmt.Errorf("view collections: %v", err)
|
||||
}
|
||||
d := struct {
|
||||
*UserPage
|
||||
Collections *[]Collection
|
||||
|
@ -786,11 +797,13 @@ func viewCollections(app *App, u *User, w http.ResponseWriter, r *http.Request)
|
|||
UsedCollections, TotalCollections int
|
||||
|
||||
NewBlogsDisabled bool
|
||||
Suspended bool
|
||||
}{
|
||||
UserPage: NewUserPage(app, r, u, u.Username+"'s Blogs", f),
|
||||
Collections: c,
|
||||
UsedCollections: int(uc),
|
||||
NewBlogsDisabled: !app.cfg.App.CanCreateBlogs(uc),
|
||||
Suspended: suspended,
|
||||
}
|
||||
d.UserPage.SetMessaging(u)
|
||||
showUserPage(w, "collections", d)
|
||||
|
@ -808,13 +821,20 @@ func viewEditCollection(app *App, u *User, w http.ResponseWriter, r *http.Reques
|
|||
return ErrCollectionNotFound
|
||||
}
|
||||
|
||||
suspended, err := app.db.IsUserSuspended(u.ID)
|
||||
if err != nil {
|
||||
log.Error("view edit collection %v", err)
|
||||
return fmt.Errorf("view edit collection: %v", err)
|
||||
}
|
||||
flashes, _ := getSessionFlashes(app, w, r, nil)
|
||||
obj := struct {
|
||||
*UserPage
|
||||
*Collection
|
||||
Suspended bool
|
||||
}{
|
||||
UserPage: NewUserPage(app, r, u, "Edit "+c.DisplayTitle(), flashes),
|
||||
Collection: c,
|
||||
Suspended: suspended,
|
||||
}
|
||||
|
||||
showUserPage(w, "collection", obj)
|
||||
|
@ -976,17 +996,24 @@ func viewStats(app *App, u *User, w http.ResponseWriter, r *http.Request) error
|
|||
titleStats = c.DisplayTitle() + " "
|
||||
}
|
||||
|
||||
suspended, err := app.db.IsUserSuspended(u.ID)
|
||||
if err != nil {
|
||||
log.Error("view stats: %v", err)
|
||||
return err
|
||||
}
|
||||
obj := struct {
|
||||
*UserPage
|
||||
VisitsBlog string
|
||||
Collection *Collection
|
||||
TopPosts *[]PublicPost
|
||||
APFollowers int
|
||||
Suspended bool
|
||||
}{
|
||||
UserPage: NewUserPage(app, r, u, titleStats+"Stats", flashes),
|
||||
VisitsBlog: alias,
|
||||
Collection: c,
|
||||
TopPosts: topPosts,
|
||||
Suspended: suspended,
|
||||
}
|
||||
if app.cfg.App.Federation {
|
||||
folls, err := app.db.GetAPFollowers(c)
|
||||
|
@ -1026,7 +1053,7 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||
Email: fullUser.EmailClear(app.keys),
|
||||
HasPass: passIsSet,
|
||||
IsLogOut: r.FormValue("logout") == "1",
|
||||
Suspended: fullUser.Suspended,
|
||||
Suspended: fullUser.Status == UserSuspended,
|
||||
}
|
||||
|
||||
showUserPage(w, "settings", obj)
|
||||
|
|
|
@ -82,7 +82,7 @@ func handleFetchCollectionActivities(app *App, w http.ResponseWriter, r *http.Re
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(c.OwnerID)
|
||||
if err != nil {
|
||||
log.Error("fetch collection inbox: get owner: %v", err)
|
||||
log.Error("fetch collection activities: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -115,7 +115,7 @@ func handleFetchCollectionOutbox(app *App, w http.ResponseWriter, r *http.Reques
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(c.OwnerID)
|
||||
if err != nil {
|
||||
log.Error("fetch collection inbox: get owner: %v", err)
|
||||
log.Error("fetch collection outbox: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -176,7 +176,7 @@ func handleFetchCollectionFollowers(app *App, w http.ResponseWriter, r *http.Req
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(c.OwnerID)
|
||||
if err != nil {
|
||||
log.Error("fetch collection inbox: get owner: %v", err)
|
||||
log.Error("fetch collection followers: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -230,7 +230,7 @@ func handleFetchCollectionFollowing(app *App, w http.ResponseWriter, r *http.Req
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(c.OwnerID)
|
||||
if err != nil {
|
||||
log.Error("fetch collection inbox: get owner: %v", err)
|
||||
log.Error("fetch collection following: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -272,7 +272,7 @@ func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(c.OwnerID)
|
||||
if err != nil {
|
||||
log.Error("fetch collection inbox: get owner: %v", err)
|
||||
log.Error("fetch collection inbox: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
|
13
admin.go
13
admin.go
|
@ -230,28 +230,27 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque
|
|||
return nil
|
||||
}
|
||||
|
||||
func handleAdminToggleUserSuspended(app *App, u *User, w http.ResponseWriter, r *http.Request) error {
|
||||
func handleAdminToggleUserStatus(app *App, u *User, w http.ResponseWriter, r *http.Request) error {
|
||||
vars := mux.Vars(r)
|
||||
username := vars["username"]
|
||||
if username == "" {
|
||||
return impart.HTTPError{http.StatusFound, "/admin/users"}
|
||||
}
|
||||
|
||||
userToToggle, err := app.db.GetUserForAuth(username)
|
||||
user, err := app.db.GetUserForAuth(username)
|
||||
if err != nil {
|
||||
log.Error("failed to get user: %v", err)
|
||||
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get user from username: %v", err)}
|
||||
}
|
||||
if userToToggle.Suspended {
|
||||
err = app.db.SetUserSuspended(userToToggle.ID, false)
|
||||
if user.Status == UserSuspended {
|
||||
err = app.db.SetUserStatus(user.ID, UserActive)
|
||||
} else {
|
||||
err = app.db.SetUserSuspended(userToToggle.ID, true)
|
||||
err = app.db.SetUserStatus(user.ID, UserSuspended)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("toggle user suspended: %v", err)
|
||||
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not toggle user suspended: %v")}
|
||||
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not toggle user status: %v")}
|
||||
}
|
||||
// TODO: invalidate sessions
|
||||
return impart.HTTPError{http.StatusFound, fmt.Sprintf("/admin/user/%s#status", username)}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ type (
|
|||
CurrentPage int
|
||||
TotalPages int
|
||||
Format *CollectionFormat
|
||||
Suspended bool
|
||||
}
|
||||
SubmittedCollection struct {
|
||||
// Data used for updating a given collection
|
||||
|
@ -398,7 +399,7 @@ func newCollection(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(userID)
|
||||
if err != nil {
|
||||
log.Error("new collection: get user: %v", err)
|
||||
log.Error("new collection: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -486,6 +487,7 @@ func fetchCollection(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
res.Owner = u
|
||||
}
|
||||
}
|
||||
// TODO: check suspended
|
||||
app.db.GetPostsCount(res, isCollOwner)
|
||||
// Strip non-public information
|
||||
res.Collection.ForPublic()
|
||||
|
@ -738,14 +740,10 @@ func handleViewCollection(app *App, w http.ResponseWriter, r *http.Request) erro
|
|||
|
||||
suspended, err := app.db.IsUserSuspended(c.OwnerID)
|
||||
if err != nil {
|
||||
log.Error("view collection: get owner: %v", err)
|
||||
log.Error("view collection: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
|
||||
if suspended {
|
||||
return ErrCollectionNotFound
|
||||
}
|
||||
|
||||
// Serve ActivityStreams data now, if requested
|
||||
if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
|
||||
ac := c.PersonObject()
|
||||
|
@ -802,6 +800,10 @@ func handleViewCollection(app *App, w http.ResponseWriter, r *http.Request) erro
|
|||
log.Error("Error getting user for collection: %v", err)
|
||||
}
|
||||
}
|
||||
if !isOwner && suspended {
|
||||
return ErrCollectionNotFound
|
||||
}
|
||||
displayPage.Suspended = isOwner && suspended
|
||||
displayPage.Owner = owner
|
||||
coll.Owner = displayPage.Owner
|
||||
|
||||
|
@ -853,10 +855,6 @@ func handleViewCollectionTag(app *App, w http.ResponseWriter, r *http.Request) e
|
|||
return err
|
||||
}
|
||||
|
||||
if u.Suspended {
|
||||
return ErrCollectionNotFound
|
||||
}
|
||||
|
||||
page := getCollectionPage(vars)
|
||||
|
||||
c, err := processCollectionPermissions(app, cr, u, w, r)
|
||||
|
@ -908,6 +906,10 @@ func handleViewCollectionTag(app *App, w http.ResponseWriter, r *http.Request) e
|
|||
log.Error("Error getting user for collection: %v", err)
|
||||
}
|
||||
}
|
||||
if !isOwner && u.Status == UserSuspended {
|
||||
return ErrCollectionNotFound
|
||||
}
|
||||
displayPage.Suspended = u.Status == UserSuspended
|
||||
displayPage.Owner = owner
|
||||
coll.Owner = displayPage.Owner
|
||||
// Add more data
|
||||
|
@ -946,7 +948,7 @@ func existingCollection(app *App, w http.ResponseWriter, r *http.Request) error
|
|||
collAlias := vars["alias"]
|
||||
isWeb := r.FormValue("web") == "1"
|
||||
|
||||
var u *User
|
||||
u := &User{}
|
||||
if reqJSON && !isWeb {
|
||||
// Ensure an access token was given
|
||||
accessToken := r.Header.Get("Authorization")
|
||||
|
@ -963,7 +965,7 @@ func existingCollection(app *App, w http.ResponseWriter, r *http.Request) error
|
|||
|
||||
suspended, err := app.db.IsUserSuspended(u.ID)
|
||||
if err != nil {
|
||||
log.Error("existing collection: get user suspended status: %v", err)
|
||||
log.Error("existing collection: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
|
||||
|
|
29
database.go
29
database.go
|
@ -296,7 +296,7 @@ func (db *datastore) CreateCollection(cfg *config.Config, alias, title string, u
|
|||
func (db *datastore) GetUserByID(id int64) (*User, error) {
|
||||
u := &User{ID: id}
|
||||
|
||||
err := db.QueryRow("SELECT username, password, email, created, suspended FROM users WHERE id = ?", id).Scan(&u.Username, &u.HashedPass, &u.Email, &u.Created, &u.Suspended)
|
||||
err := db.QueryRow("SELECT username, password, email, created, status FROM users WHERE id = ?", id).Scan(&u.Username, &u.HashedPass, &u.Email, &u.Created, &u.Status)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return nil, ErrUserNotFound
|
||||
|
@ -313,16 +313,16 @@ func (db *datastore) GetUserByID(id int64) (*User, error) {
|
|||
func (db *datastore) IsUserSuspended(id int64) (bool, error) {
|
||||
u := &User{ID: id}
|
||||
|
||||
err := db.QueryRow("SELECT suspended FROM users WHERE id = ?", id).Scan(&u.Suspended)
|
||||
err := db.QueryRow("SELECT status FROM users WHERE id = ?", id).Scan(&u.Status)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return false, ErrUserNotFound
|
||||
return false, fmt.Errorf("is user suspended: %v", ErrUserNotFound)
|
||||
case err != nil:
|
||||
log.Error("Couldn't SELECT user password: %v", err)
|
||||
return false, err
|
||||
return false, fmt.Errorf("is user suspended: %v", err)
|
||||
}
|
||||
|
||||
return u.Suspended, nil
|
||||
return u.Status == UserSuspended, nil
|
||||
}
|
||||
|
||||
// DoesUserNeedAuth returns true if the user hasn't provided any methods for
|
||||
|
@ -364,7 +364,7 @@ func (db *datastore) IsUserPassSet(id int64) (bool, error) {
|
|||
func (db *datastore) GetUserForAuth(username string) (*User, error) {
|
||||
u := &User{Username: username}
|
||||
|
||||
err := db.QueryRow("SELECT id, password, email, created, suspended FROM users WHERE username = ?", username).Scan(&u.ID, &u.HashedPass, &u.Email, &u.Created, &u.Suspended)
|
||||
err := db.QueryRow("SELECT id, password, email, created, status FROM users WHERE username = ?", username).Scan(&u.ID, &u.HashedPass, &u.Email, &u.Created, &u.Status)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
// Check if they've entered the wrong, unnormalized username
|
||||
|
@ -387,7 +387,7 @@ func (db *datastore) GetUserForAuth(username string) (*User, error) {
|
|||
func (db *datastore) GetUserForAuthByID(userID int64) (*User, error) {
|
||||
u := &User{ID: userID}
|
||||
|
||||
err := db.QueryRow("SELECT id, password, email, created, suspended FROM users WHERE id = ?", u.ID).Scan(&u.ID, &u.HashedPass, &u.Email, &u.Created, &u.Suspended)
|
||||
err := db.QueryRow("SELECT id, password, email, created, status FROM users WHERE id = ?", u.ID).Scan(&u.ID, &u.HashedPass, &u.Email, &u.Created, &u.Status)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return nil, ErrUserNotFound
|
||||
|
@ -1650,7 +1650,7 @@ func (db *datastore) GetTotalCollections() (collCount int64, err error) {
|
|||
SELECT COUNT(*)
|
||||
FROM collections c
|
||||
LEFT JOIN users u ON u.id = c.owner_id
|
||||
WHERE u.suspended = 0`).Scan(&collCount)
|
||||
WHERE u.status = 0`).Scan(&collCount)
|
||||
if err != nil {
|
||||
log.Error("Unable to fetch collections count: %v", err)
|
||||
}
|
||||
|
@ -1662,7 +1662,7 @@ func (db *datastore) GetTotalPosts() (postCount int64, err error) {
|
|||
SELECT COUNT(*)
|
||||
FROM posts p
|
||||
LEFT JOIN users u ON u.id = p.owner_id
|
||||
WHERE u.Suspended = 0`).Scan(&postCount)
|
||||
WHERE u.status = 0`).Scan(&postCount)
|
||||
if err != nil {
|
||||
log.Error("Unable to fetch posts count: %v", err)
|
||||
}
|
||||
|
@ -2384,7 +2384,7 @@ func (db *datastore) GetAllUsers(page uint) (*[]User, error) {
|
|||
limitStr = fmt.Sprintf("%d, %d", (page-1)*adminUsersPerPage, adminUsersPerPage)
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT id, username, created, suspended FROM users ORDER BY created DESC LIMIT " + limitStr)
|
||||
rows, err := db.Query("SELECT id, username, created, status FROM users ORDER BY created DESC LIMIT " + limitStr)
|
||||
if err != nil {
|
||||
log.Error("Failed selecting from users: %v", err)
|
||||
return nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't retrieve all users."}
|
||||
|
@ -2394,7 +2394,7 @@ func (db *datastore) GetAllUsers(page uint) (*[]User, error) {
|
|||
users := []User{}
|
||||
for rows.Next() {
|
||||
u := User{}
|
||||
err = rows.Scan(&u.ID, &u.Username, &u.Created, &u.Suspended)
|
||||
err = rows.Scan(&u.ID, &u.Username, &u.Created, &u.Status)
|
||||
if err != nil {
|
||||
log.Error("Failed scanning GetAllUsers() row: %v", err)
|
||||
break
|
||||
|
@ -2431,10 +2431,11 @@ func (db *datastore) GetUserLastPostTime(id int64) (*time.Time, error) {
|
|||
return &t, nil
|
||||
}
|
||||
|
||||
func (db *datastore) SetUserSuspended(id int64, suspend bool) error {
|
||||
_, err := db.Exec("UPDATE users SET suspended = ? WHERE id = ?", suspend, id)
|
||||
// SetUserStatus changes a user's status in the database. see Users.UserStatus
|
||||
func (db *datastore) SetUserStatus(id int64, status UserStatus) error {
|
||||
_, err := db.Exec("UPDATE users SET status = ? WHERE id = ?", status, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update user suspended status: %v", err)
|
||||
return fmt.Errorf("failed to update user status: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ func handleCreateUserInvite(app *App, u *User, w http.ResponseWriter, r *http.Re
|
|||
muVal := r.FormValue("uses")
|
||||
expVal := r.FormValue("expires")
|
||||
|
||||
if u.Suspended {
|
||||
if u.Status == UserSuspended {
|
||||
return ErrUserSuspended
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ func (m *migration) Migrate(db *datastore) error {
|
|||
var migrations = []Migration{
|
||||
New("support user invites", supportUserInvites), // -> V1 (v0.8.0)
|
||||
New("support dynamic instance pages", supportInstancePages), // V1 -> V2 (v0.9.0)
|
||||
New("support users suspension", supportUserSuspension), // V2 -> V3 ()
|
||||
New("support users suspension", supportUserStatus), // V2 -> V3 (v0.11.0)
|
||||
}
|
||||
|
||||
// CurrentVer returns the current migration version the application is on
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
|
||||
package migrations
|
||||
|
||||
func supportUserSuspension(db *datastore) error {
|
||||
func supportUserStatus(db *datastore) error {
|
||||
t, err := db.Begin()
|
||||
|
||||
_, err = t.Exec(`ALTER TABLE users ADD COLUMN suspended ` + db.typeBool() + ` DEFAULT '0' NOT NULL`)
|
||||
_, err = t.Exec(`ALTER TABLE users ADD COLUMN status ` + db.typeInt() + ` DEFAULT '0' NOT NULL`)
|
||||
if err != nil {
|
||||
t.Rollback()
|
||||
return err
|
||||
|
|
22
posts.go
22
posts.go
|
@ -383,7 +383,7 @@ func handleViewPost(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
|
||||
suspended, err := app.db.IsUserSuspended(ownerID.Int64)
|
||||
if err != nil {
|
||||
log.Error("view post: get collection owner: %v", err)
|
||||
log.Error("view post: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
|
||||
|
@ -509,7 +509,7 @@ func newPost(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(userID)
|
||||
if err != nil {
|
||||
log.Error("new post: get user: %v", err)
|
||||
log.Error("new post: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -683,7 +683,7 @@ func existingPost(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
|
||||
suspended, err := app.db.IsUserSuspended(userID)
|
||||
if err != nil {
|
||||
log.Error("existing post: get user: %v", err)
|
||||
log.Error("existing post: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -886,7 +886,7 @@ func addPost(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
|
||||
suspended, err := app.db.IsUserSuspended(ownerID)
|
||||
if err != nil {
|
||||
log.Error("add post: get user: %v", err)
|
||||
log.Error("add post: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -989,7 +989,7 @@ func pinPost(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
|
||||
suspended, err := app.db.IsUserSuspended(userID)
|
||||
if err != nil {
|
||||
log.Error("pin post: get user: %v", err)
|
||||
log.Error("pin post: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
if suspended {
|
||||
|
@ -1063,7 +1063,7 @@ func fetchPost(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
}
|
||||
suspended, err := app.db.IsUserSuspended(ownerID)
|
||||
if err != nil {
|
||||
log.Error("fetch post: get owner: %v", err)
|
||||
log.Error("fetch post: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
|
||||
|
@ -1333,13 +1333,10 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
|
|||
|
||||
suspended, err := app.db.IsUserSuspended(c.OwnerID)
|
||||
if err != nil {
|
||||
log.Error("view collection post: get owner: %v", err)
|
||||
log.Error("view collection post: %v", err)
|
||||
return ErrInternalGeneral
|
||||
}
|
||||
|
||||
if suspended {
|
||||
return ErrPostNotFound
|
||||
}
|
||||
// Check collection permissions
|
||||
if c.IsPrivate() && (u == nil || u.ID != c.OwnerID) {
|
||||
return ErrPostNotFound
|
||||
|
@ -1396,6 +1393,9 @@ Are you sure it was ever here?`,
|
|||
p.Collection = coll
|
||||
p.IsTopLevel = app.cfg.App.SingleUser
|
||||
|
||||
if !p.IsOwner && suspended {
|
||||
return ErrPostNotFound
|
||||
}
|
||||
// Check if post has been unpublished
|
||||
if p.Content == "" && p.Title.String == "" {
|
||||
return impart.HTTPError{http.StatusGone, "Post was unpublished."}
|
||||
|
@ -1445,12 +1445,14 @@ Are you sure it was ever here?`,
|
|||
IsFound bool
|
||||
IsAdmin bool
|
||||
CanInvite bool
|
||||
Suspended bool
|
||||
}{
|
||||
PublicPost: p,
|
||||
StaticPage: pageForReq(app, r),
|
||||
IsOwner: cr.isCollOwner,
|
||||
IsCustomDomain: cr.isCustomDomain,
|
||||
IsFound: postFound,
|
||||
Suspended: suspended,
|
||||
}
|
||||
tp.IsAdmin = u != nil && u.IsAdmin()
|
||||
tp.CanInvite = canUserInvite(app.cfg, tp.IsAdmin)
|
||||
|
|
2
read.go
2
read.go
|
@ -71,7 +71,7 @@ func (app *App) FetchPublicPosts() (interface{}, error) {
|
|||
FROM collections c
|
||||
LEFT JOIN posts p ON p.collection_id = c.id
|
||||
LEFT JOIN users u ON u.id = p.owner_id
|
||||
WHERE c.privacy = 1 AND (p.created >= ` + app.db.dateSub(3, "month") + ` AND p.created <= ` + app.db.now() + ` AND pinned_position IS NULL) AND u.suspended = 0
|
||||
WHERE c.privacy = 1 AND (p.created >= ` + app.db.dateSub(3, "month") + ` AND p.created <= ` + app.db.now() + ` AND pinned_position IS NULL) AND u.status = 0
|
||||
ORDER BY p.created DESC`)
|
||||
if err != nil {
|
||||
log.Error("Failed selecting from posts: %v", err)
|
||||
|
|
|
@ -144,7 +144,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
|
|||
write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET")
|
||||
write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET")
|
||||
write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET")
|
||||
write.HandleFunc("/admin/user/{username}", handler.Admin(handleAdminToggleUserSuspended)).Methods("POST")
|
||||
write.HandleFunc("/admin/user/{username}/status", handler.Admin(handleAdminToggleUserStatus)).Methods("POST")
|
||||
write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET")
|
||||
write.HandleFunc("/admin/page/{slug}", handler.Admin(handleViewAdminPage)).Methods("GET")
|
||||
write.HandleFunc("/admin/update/config", handler.AdminApper(handleAdminUpdateConfig)).Methods("POST")
|
||||
|
|
|
@ -225,7 +225,6 @@ CREATE TABLE IF NOT EXISTS `users` (
|
|||
`password` char(60) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
|
||||
`email` varbinary(255) DEFAULT NULL,
|
||||
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`suspended` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
|
|
@ -214,8 +214,7 @@ CREATE TABLE IF NOT EXISTS `users` (
|
|||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT DEFAULT NULL,
|
||||
created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
suspended INTEGER NOT NULL DEFAULT 0
|
||||
created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
|
12
templates.go
12
templates.go
|
@ -11,10 +11,6 @@
|
|||
package writefreely
|
||||
|
||||
import (
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/writeas/web-core/l10n"
|
||||
"github.com/writeas/web-core/log"
|
||||
"github.com/writeas/writefreely/config"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -22,6 +18,11 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/writeas/web-core/l10n"
|
||||
"github.com/writeas/web-core/log"
|
||||
"github.com/writeas/writefreely/config"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -63,6 +64,7 @@ func initTemplate(parentDir, name string) {
|
|||
filepath.Join(parentDir, templatesDir, name+".tmpl"),
|
||||
filepath.Join(parentDir, templatesDir, "include", "footer.tmpl"),
|
||||
filepath.Join(parentDir, templatesDir, "base.tmpl"),
|
||||
filepath.Join(parentDir, templatesDir, "user", "include", "suspended.tmpl"),
|
||||
}
|
||||
if name == "collection" || name == "collection-tags" || name == "chorus-collection" {
|
||||
// These pages list out collection posts, so we also parse templatesDir + "include/posts.tmpl"
|
||||
|
@ -86,6 +88,7 @@ func initPage(parentDir, path, key string) {
|
|||
path,
|
||||
filepath.Join(parentDir, templatesDir, "include", "footer.tmpl"),
|
||||
filepath.Join(parentDir, templatesDir, "base.tmpl"),
|
||||
filepath.Join(parentDir, templatesDir, "user", "include", "suspended.tmpl"),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -98,6 +101,7 @@ func initUserPage(parentDir, path, key string) {
|
|||
path,
|
||||
filepath.Join(parentDir, templatesDir, "user", "include", "header.tmpl"),
|
||||
filepath.Join(parentDir, templatesDir, "user", "include", "footer.tmpl"),
|
||||
filepath.Join(parentDir, templatesDir, "user", "include", "suspended.tmpl"),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,9 @@ article time.dt-published {
|
|||
|
||||
{{template "user-navigation" .}}
|
||||
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<article id="post-body" class="{{.Font}} h-entry">{{if .IsScheduled}}<p class="badge">Scheduled</p>{{end}}{{if .Title.String}}<h2 id="title" class="p-name">{{.FormattedDisplayTitle}}</h2>{{end}}{{/* TODO: check format: if .Collection.Format.ShowDates*/}}<time class="dt-published" datetime="{{.Created}}" pubdate itemprop="datePublished" content="{{.Created}}">{{.DisplayDate}}</time><div class="e-content">{{.HTMLContent}}</div></article>
|
||||
|
||||
{{ if .Collection.ShowFooterBranding }}
|
||||
|
|
|
@ -61,6 +61,9 @@ body#collection header nav.tabs a:first-child {
|
|||
<body id="collection" itemscope itemtype="http://schema.org/WebPage">
|
||||
{{template "user-navigation" .}}
|
||||
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<header>
|
||||
<h1 dir="{{.Direction}}" id="blog-title"><a href="/{{if .IsTopLevel}}{{else}}{{.Prefix}}{{.Alias}}/{{end}}" class="h-card p-author u-url" rel="me author">{{.DisplayTitle}}</a></h1>
|
||||
{{if .Description}}<p class="description p-note">{{.Description}}</p>{{end}}
|
||||
|
|
|
@ -59,6 +59,9 @@
|
|||
</nav>
|
||||
</header>
|
||||
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<article id="post-body" class="{{.Font}} h-entry {{if not .IsFound}}error-page{{end}}">{{if .IsScheduled}}<p class="badge">Scheduled</p>{{end}}{{if .Title.String}}<h2 id="title" class="p-name">{{.FormattedDisplayTitle}}</h2>{{end}}<div class="e-content">{{.HTMLContent}}</div></article>
|
||||
|
||||
{{ if .Collection.ShowFooterBranding }}
|
||||
|
|
|
@ -53,6 +53,9 @@
|
|||
</nav>
|
||||
</header>
|
||||
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
{{if .Posts}}<section id="wrapper" itemscope itemtype="http://schema.org/Blog">{{else}}<div id="wrapper">{{end}}
|
||||
<h1>{{.Tag}}</h1>
|
||||
{{template "posts" .}}
|
||||
|
|
|
@ -62,6 +62,9 @@
|
|||
</ul></nav>{{end}}
|
||||
|
||||
<header>
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<h1 dir="{{.Direction}}" id="blog-title">{{if .Posts}}{{else}}<span class="writeas-prefix"><a href="/">write.as</a></span> {{end}}<a href="/{{if .IsTopLevel}}{{else}}{{.Prefix}}{{.Alias}}/{{end}}" class="h-card p-author u-url" rel="me author">{{.DisplayTitle}}</a></h1>
|
||||
{{if .Description}}<p class="description p-note">{{.Description}}</p>{{end}}
|
||||
{{/*if not .Public/*}}
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
</head>
|
||||
<body id="collection" itemscope itemtype="http://schema.org/WebPage">
|
||||
{{if .Suspended}}
|
||||
{{template "user-supsended"}}
|
||||
{{end}}
|
||||
<header>
|
||||
<h1 dir="{{.Direction}}" id="blog-title"><a href="/{{.Alias}}/" class="h-card p-author u-url" rel="me author">{{.DisplayTitle}}</a></h1>
|
||||
</header>
|
||||
|
|
|
@ -36,6 +36,9 @@
|
|||
</head>
|
||||
<body id="post">
|
||||
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<header>
|
||||
<h1 dir="{{.Direction}}"><a href="/">{{.SiteName}}</a></h1>
|
||||
<nav>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<td style="text-align:center">
|
||||
<a
|
||||
href="/admin/user/{{.Username}}#status"
|
||||
title="View or change account status">{{if .Suspended}}suspended{{else}}active{{end}}</a></td>
|
||||
title="View or change account status">{{if eq .Status 1}}suspended{{else}}active{{end}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
|
|
|
@ -57,10 +57,10 @@ td.active-suspend > input[type="submit"] {
|
|||
<td>{{if .LastPost}}{{.LastPost}}{{else}}Never{{end}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<form action="/admin/user/{{.User.Username}}" method="POST">
|
||||
<form action="/admin/user/{{.User.Username}}/status" method="POST">
|
||||
<a id="status"/>
|
||||
<th>Status</th>
|
||||
{{if .User.Suspended}}
|
||||
{{if eq .User.Status 1}}
|
||||
<td class="active-suspend"><p>User is currently Suspended</p><input type="submit" value="Activate"/></td>
|
||||
{{else}}
|
||||
<td class="active-suspend">
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
{{if .Flashes}}<ul class="errors">
|
||||
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
|
||||
</ul>{{end}}
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
|
||||
<h2 id="posts-header">drafts</h2>
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
<div class="content-container snug">
|
||||
<div id="overlay"></div>
|
||||
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<h2>Customize {{.DisplayTitle}} <a href="{{if .SingleUser}}/{{else}}/{{.Alias}}/{{end}}">view blog</a></h2>
|
||||
|
||||
{{if .Flashes}}<ul class="errors">
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
|
||||
</ul>{{end}}
|
||||
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<h2>blogs</h2>
|
||||
<ul class="atoms collections">
|
||||
{{range $i, $el := .Collections}}<li class="collection"><h3>
|
||||
|
|
6
templates/user/include/suspended.tmpl
Normal file
6
templates/user/include/suspended.tmpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
{{define "user-suspended"}}
|
||||
<div class="alert info">
|
||||
<p>This account is currently suspended.</p>
|
||||
<p>Please contact the instance administrator to discuss reactivation.</p>
|
||||
</div>
|
||||
{{end}}
|
|
@ -7,17 +7,14 @@ h3 { font-weight: normal; }
|
|||
.section > *:not(input) { font-size: 0.86em; }
|
||||
</style>
|
||||
<div class="content-container snug regular">
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<h2>{{if .IsLogOut}}Before you go...{{else}}Account Settings {{if .IsAdmin}}<a href="/admin">admin settings</a>{{end}}{{end}}</h2>
|
||||
{{if .Flashes}}<ul class="errors">
|
||||
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
|
||||
</ul>{{end}}
|
||||
|
||||
{{if .Suspended}}
|
||||
<div class="alert info">
|
||||
<p>This account is currently suspended.</p>
|
||||
<p>Please contact the instance administrator to discuss reactivation.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{ if .IsLogOut }}
|
||||
<div class="alert info">
|
||||
<p class="introduction">Please add an <strong>email address</strong> and/or <strong>passphrase</strong> so you can log in again later.</p>
|
||||
|
|
|
@ -17,6 +17,9 @@ td.none {
|
|||
</style>
|
||||
|
||||
<div class="content-container snug">
|
||||
{{if .Suspended}}
|
||||
{{template "user-suspended"}}
|
||||
{{end}}
|
||||
<h2 id="posts-header">{{if .Collection}}{{.Collection.DisplayTitle}} {{end}}Stats</h2>
|
||||
|
||||
<p>Stats for all time.</p>
|
||||
|
|
9
users.go
9
users.go
|
@ -19,6 +19,13 @@ import (
|
|||
"github.com/writeas/writefreely/key"
|
||||
)
|
||||
|
||||
type UserStatus int
|
||||
|
||||
const (
|
||||
UserActive = iota
|
||||
UserSuspended
|
||||
)
|
||||
|
||||
type (
|
||||
userCredentials struct {
|
||||
Alias string `json:"alias" schema:"alias"`
|
||||
|
@ -59,7 +66,7 @@ type (
|
|||
HasPass bool `json:"has_pass"`
|
||||
Email zero.String `json:"email"`
|
||||
Created time.Time `json:"created"`
|
||||
Suspended bool `json:"suspended"`
|
||||
Status UserStatus `json:"status"`
|
||||
|
||||
clearEmail string `json:"email"`
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue